泛型的一般表达
ArrayList<String> files = new ArrayList<>();
public class Classname<T> //T:类型参数
定义一个带有参数类型的简单方法:
class ArrayAlg
{
public static <T> T getMiddle(T....a)
{
return .......;
}
}
注意:类型修饰符放在修饰符后面,返回类型前面。
调用这个方法的语句:
String middle = ArrayAlg.<String>getMiddle("John","Q","Public");//可以省略<String>.
类型变量的限定:public static <T extends ClassName> T Func(ArguType ......argu);
一个类型或通配符可以有多个限定,用&分开。
public class Pair<T>
{ private Object first;
private Object second;
public Pair(Object first,Object second)
{ this.first = first;
this.second = second;}
public Object getFirst(){return first;}
public Object getSecond(){return second;}
public void setFirst(Object newValue) {first = newValue;}
public void secSecond(Object newValue) {second = newValue;}
}
泛型代码和虚拟机
虚拟机没有泛型类型对象——所有对象都属于普通类。
类型擦除
擦除类型变量,并替换为限定类型,原始类型用第一个限定的类型变量来替换,无限定类型的变量就用Object替换。
当extends后有多个限定变量时,应该把标签接口放在尾部。标签接口:没有方法的接口。
翻译泛型表达式
当程序调用泛型表达式,如果擦除返回类型,编译器插入强制类型转换。
例如:Pair<Employee> buddies = Pair(first,second);//这里的first和second必须是<>内的限定类型。即first = new Employee(...);
Employee body = buddies.getfirst();编译器把这个方法调用翻译为两条虚拟机指令:
1.对原始方法Pair.getFirst()的调用。
2.将返回的Object类型转换为Employee;
存取泛型域的时候也要插入强制类型转换,例如:Employee buddy = buddies.first;
翻译泛型方法:需要记住桥方法
个人理解:多态与擦除出现冲突,可以理解为子类重载了父类的一个方法,导致最后虚拟机不知道应该调用哪个方法(比如说,Employee 对象可以当作Object对象),然后使用合成桥方法类解决这个问题。(合成桥方法是虚拟机内部的机制,是由虚拟机自动完成的。)桥方法会使用父类的那个函数来调用子类的函数。
泛型的约束与局限性
不能用基本类型实例化类型参数。
例:Pair<double> 语法错误,只能用Pair<Double> (装箱)
运行时类型查询只适用于原始类型
不能创建参数化类型的数组
如果要收集参数化类型对象,只有一个安全而有效的方法:ArrayList<Pair<ClassName>>
Varargs警告
@safeVarargs
public static <T> void addAll(Collection<T>,T...ts)
这时可以提供泛型类型来调用这个方法。
不能实例化类型变量
new T[...],new T(....),T.class等都是错误的,因为T会擦除为Object。
不能构造泛型数组
泛型类的静态上下文中类型变量无效
不能抛出或者捕获泛型类的实例
甚至连泛型类扩展Throwable都不合法。例:public class Problem<T> extends Exception {.......}//ERROR
catch子句不能使用类型变量。即catch(T e)//ERROR
public static <T extends Throwable> void doWork(T t) throws T//OK
可以消除对受查异常的检查
注意擦除后的冲突
要想支持擦除的转换,就要强行限制一个类或类型变量不能同时成为两个接口类的子类,而这两个接口是同一接口的不同参数化。例:
class Employee implements Comparable<Employee> {......};
class Manager extends Employee implements Comparable<Manager>;
是不合法的,因为对于Manager对象来说,compareTo()方法有两个可以调用。
泛型的继承
无论S与T有什么关系,一般情况,Pair<T>,Pair<S>都不会有什么关系。泛型的继承和普通的继承基本相同,extends语句。
通配符类型
Pair<? extends ClassName> 表示任何泛型Pair类型,它的类型参数是ClassName的子类。//可以使用访问器,无法使用构造器.
Pair<? super ClassName> 表示类型参数是ClassName的所有超类型。//可以使用构造器,不能使用访问器。
带有超类型限定的通配符可以向泛型对象写入,带有子类限定的通配符可以从泛型对象中读取。
主要原因是:父类可以引用子类,但是子类不能引用父类。
无限定通配符 Pair<?>.类型Pair<?>有以下方法: ? getFirst(); void setFirst(?);
getFirst()的返回值只能赋给Object对象。setFirst不能被调用,甚至不能被Object调用。Pair<?>和Pair本质的不同在于:可以用任意Object对象调用原始Pair类的setFirst方法。注释:可以调用setFirst(null).例:
public static boolean hasNulls(Pair<?> p)
{return p.getFirst() == null || p.getSecond == null;}
注:引用子类的父类在调用之内与自己相同的方法时,使用的是自己的方法(前提是没有进行类型转换)。