泛型方法
有关如何定义泛型类就不在此写出来了,这篇文章主要还是记录一下有关泛型方法的相关内容:泛型方法可以出现普通类中,也可以出现在泛型类中。调用泛型方法是,应该在方法名前的尖括号中放入类型变量,如:
String m = Arrayflag.<String>getMiddle("AB","CD","EF") public static <T> T getMiddle(T... a){ return a[a.length/2]; }
在大多数情况下可以省略<String>参数。编译器可以通过参数的类型与泛类型进行对比可以推测出T是String。
如果对于编译器最终会给参数匹配到何种类型可以通过引入错误,来观察编译器给出的错误报告。比如:将Arrayflag.getMiddle(“Hello”,0,null)的结果赋给JButton,可以得到一个错误报告可以看到追踪到了三个类型:Object,Serialiable,Comparable,编译器会试图寻找参数的共同父类或接口,并调用方法。
类型变量的限定
<T extends BoundingType> 限定类型之间用&分隔,可有多个接口,但之多有一个类,如果有类应该将类放在第一位。在类型擦除以后,替换为限定类型,用第一个限定类型替换。(无限定类型时则用Object替换)
Pair<Employee> p = new Pair<>();
Employee e = p.getFirst();
擦除类型后将返回Object类型,编译器会自动插入强制类型转换,即编译器把这个调用翻译成了两条虚拟机指令:1.原始方法调用 pair.getFirst() 2.将返回的Object对象进行Employee强制转换。如果是访问域,也是如此进行的。
有关类型擦除和多态的冲突
public static <T extends Comparable> T min(T[] a) //这是一个方法族,在类型擦除后
public static Comparable min (Comparable[] a) //擦除类型后只有一个方法
class Pari<T>{
private T first;
private T second;
public void setSecond(T second){
this.second = second;
}
...
}
class DateInter extends Pair<Date>{
public void setSecond(Date second){
super.setSecond(second);
}
}
在类型擦除后,DateInter中存在另一个另一个从Pair继承而来的public void setSecond(Object second)。看起来子类中的setSecond像是重写了父类的方法但是:
Pair<Date> p = new DateInter();
p.setSecond(new Date());
//这里就无法实现多态想要达到的效果因为它会直接调用从父类继承的参数为Object类型的setSecond方法
但是编译器采用了一个很巧妙的方法,桥方法将从父类继承而来的方法改成如下形式:
public void setSecond(Object second){
this.setSecond((Date) second);
}
这样就完美的解决了泛型类型擦除和多态之间的冲突。
如果Dateinter覆盖了父类的getFirst()方法
public Date getFirst(){
return (Date)super.getFirst().clone();
}
但是在类型擦除以后,就会有两个方法Date getFirst(),Object getFirst()(这里同样会生成桥方法)。事实上我们是无法编写这样的代码并通过编译的,但是在虚拟机中,它可以通过返回类和参数类型确定一个方法,所以这里允许在虚拟机进行这样的操作。
有关泛型转换的事实
1.虚拟机中mei 泛型,只有普通的类和方法
2.所有的类型参数都用他们限定类型替换
3.桥方法合成用来维持多态
4.为保持类型安全,必要时插入强制类型转换
学习自java核心技术 卷I