首页 > 代码库 > java学习笔记(Core Java) 8 泛型

java学习笔记(Core Java) 8 泛型

泛型
一.定义一个泛型类
public class Pair<T>
{
private T _first;

public Pair(T first) {this._first = first;}

public T getfirst() {return _first;}
}
java中类型变量使用大写字母且比较短
E:元素类型 K,V 表的关键字与值的类型 T(U/S)任意类型

泛型方法:要么返回类型是泛型,要么参数是泛型
public static <T> getMiddle(T... a)
{
return a[a.length/2];
}
调用时,程序会自动识别泛型类型。偶尔会有错误

C++:与C++模板类(template)类似,但有着本质区别

二.限定一个类型变量,为类型变量提供一个父类接口,方便该类型拥有某些方法(使用extends而不是implements)
public static <T extends Compareable> T getMiddle(T ...);

可以绑定多个限定类,使用&隔开
T extends Compareable & Serializable

三.泛型代码与虚拟机
所有的泛型代码,都会提供一个原始类型。就是删除类型参数后面的泛型类型名字,替换为限定类。没有限定类的用object代替。有多个限定类的,将使用第一个限定类代替T,并继承第二个限定类
public class Pair
{
private object _first;

public Pair(object first) {this._first = first;}

public object getFirst() {return _first;}
}

C++:c++的每个模板实例化产生不同的类型,这一现象成为“模板代码膨胀”

四.类型擦出与多态调用、桥方法
翻译泛型表达式:如果擦除返回类型,则默认返回object类型,编译器将强制插入类型转换
翻译泛型方法: 泛型类父类类型擦出后,对于子类来说,重写父类的方法可能会存在破坏多态性的情况
完整的方法: public static DataInterval extends Pair<Data>
擦除类型后: public static DataInterval extends Pair //该类中所有关于T的域或方法,都会被Object代替
如果子类重写父类的某个方法:例如: setSecond,则父类参数列表为object,而子类的参数列表仍为子类定义的Data类型,这与重写的特征相违背。破坏了多态。到底是调用哪一个方法呢?
父类:setSecond(Object value);//setSecond(T value);
子类:setSecond(Data value);
setSecond(Object value);//继承的父类

//调用
DataInterval interval = new DataInterval(...);
Pair<Data> pair = interval;
pair.setSecond(aData);
由于pair引用了DataInterval对象,所以我们应该要用DataInterval.setSecond。但问题在于类型擦除与多态发生了冲突。不知道要用的是哪一个

JVM工作原理如下:
1.变量pair声明为Pair<Date>,该类型只有一个setSecond(Object)方法,虚拟机用pair引用的对象去调用这个setSecond(Object)方法。
2.pair引用的对象是DateInterval,所以将会调用DateInterval.setSecond(Object)方法,这个方法是桥方法。
3.这个桥方法会调用DateInterval.setSecond(Date)方法。

解决方法:需要在编译器内生成一个桥方法
public void setSecond(Object value)
{
setSecond(Data)value); //强制类型转换!无论怎么调用,都会调用setSecond(Data value)方法
}

如果覆盖了子类的getSecond方法;并且使用桥方法
class DateInterval extends Pair<Date> {
    public Date getSecond() {
        return (Date) super.getSecond().clone();
    }
}
擦除类型后编程:
Data getSecond();
Object getSecond();//同样也是两个方法
实际上,jvm可以通过返回类型来区分要调用哪个方法

桥方法不仅用于泛型类型; 还有, 在一个方法覆盖另一个方法时可以指定一个更严格的返回类型,指明要调用的方法:
public Employee clone() throws ClooneNotSupportedException {。。。}
实际上,Employee类有两个clone方法,object clone()以及Employee clone;合成桥方法调用了新定义的方法

java泛型转换的事实:
1.虚拟机中没有泛型,只有普通的类和方法
2.所有的类型参数都会使用限定类型转换
3.桥方法被合成保持多态
4.为保持类型安全性,必要时插入强制类型转换

五.泛型的局限性
1.不能使用及类型实例化类型参数 例如:没有Pair<double>,只有Pair<Double>,可以使用包装器包装基本类型
2.运行时类型查询只适用于原始类型
虚拟机中的对象总有一个特定的非泛型类型,所有的类型查询只产生原始类型。(instanceof,getCLass方法等.凡是涉及到使用instanceof还是强制类型转换,都会产生一个警告)
pair<String> strobg = "..."; ==>Pair
pair<Employee> Empobg = "..."; ==> Pair
if(stringobg.getClass() == Empobg.getClass()) //they is equal
3.不能创建参数化的数组
Pair<String>[] table = new Pair<String>[10]
擦出后Pair[] table可以转变为 object[]数组,如果试图存入其他类型的变量,就会抛出异常
如果要收集参数化类型对象,只有一种安全有效的方法,使用AllayList
AllayList<Pair<String>>
4.Varargs警告:如果参数列表数量可变,那么就会产生类似上述的情况出项,参数列表数目可变其实也就是将参数列表里的所有相同类型的参数包装到数组里去。这时会产生一个警告,可以使用@SuppressWarnings("unchecked")标注(javaSE 7还可以使用@SaveVarargs标注该方法)
@Savavarargs
public static<T> void addAll(Collect<T> coll, T... ts);
5.不能实例化类型变量,不能构造一个泛型数组
不能 new T(...),new T[]或者T.class,因为类型擦出后,T将变成object类型
或者object[2],new object()不是我们希望看到的,可以使用反射去创建对象
//first.T.class是不合法的
public static <T> Pair<T> makePair(Class<T> cl) //<T> 表明泛型方法
{
try{
return new Pair<>(cl.newInstance(),cl.newInstance())
}
catch(Exception ex)
{
return null
}
}
Pair<String> p = Pair.makePair(String.class);

6.泛型类的静态上下文中类型变量无效
不能在静态域或方法中引用类型变量
public class SingLeton<T>
{
private static T singleInstance; //Error

public static T getSingleInstance //Error
{
if(singleleInstance == null)
return singleInstance;
}
}

7.不能抛出或捕捉泛型类的实例
基于泛型类拓展的Throwable都是不合法的
8.注意类型擦出后的冲突

五.泛型继承
无论S,T之间有什么关系。Pair<S>和Pair<T>之间没有任何关系,不能随意调用类中的方法,解决见《六,通配符》
永远可以吧泛型类型转换为原始类型,这一点可以处理遗留代码。但是仍不保证一点安全

六.通配符类型
Pair<? extends Employee>
表示任何泛型Pair类型,它的类型参数是Employee的子类,如Pair<Manager>,但不是Pair<String>等其他类型

public static void paintBuddies(<Pair<? extends Employee> p)

类型<Pair<Manager>是<Pair<? extends Employee>的 子集

1.通配符超类型限定
2.无限顶通配符
3.通配符捕获

六、反射与泛型
Class类是泛型,例如String.class实际上是一个Class<String>类的对象(事实上,是唯一的对象)
类型参数使得Class<T>方法的返回值更具有针对性。
T newInstance()
T cast(object obj)
T[] getEnumConstances()
...
//原书 554页

java学习笔记(Core Java) 8 泛型