public class GenericSample {
private T first;
private U second;
public GenericSample(T first, U second) {
this.first = first;
this.second = second;
}
/*
* 泛型方法可以定义在普通类中,也可以定义在泛型类中
*/
public K test2(T t, L l){
return null;
}
/*
* 为泛型类型加约束,多个约束可用&分隔
*/
public static int test1(S s){
return s.capacity();
}
}
1. 泛型类与泛型方法的定义,以及类型变量限定,见GenericSample。
2. 泛型代码与虚拟机
(1)虚拟机如何看待泛型类型:
虚拟机里不存在泛型类型对象,所有对象都属于普通类。无论何时定义了一个泛型类型,都自动提供了一个原始类型(raw type),jvm使用的是原始类型。以GenericSample为例, 其原始类型是:
public class GenericSample= {
private Object first;
private Object second;
public GenericSample(Object first, Object second) {
this.first = first;
this.second = second;
}
public Object test2(Object t, Object l){
return null;
}
public static int test1(Buffer s){
return s.capacity();
}
}
(2)虚拟机如何翻译泛型表达式:
泛型类型转换成原始类型时,类型变量会被替换为限定类型的第一个(称为“擦除/erase”),如test1方法的参数类型为Buffer, 如果没有限定类型,则转换成Object类型。
当返回类型需要被擦除,调用泛型方法时虚拟机会自动插入强制转换。如
List list = new ArrayList();
String s = list.get(0);
第二行代码编译器会将其转换为两条指令:(1)调用原始类型的get方法(2)将返回值的类型由Object强制转换成String。
public static void main(String[] args) {
GenericBase sub = new GenericSub("sub");
sub.display("haha");
System.out.println(sub.getT());
}
class GenericBase {
public void display(T s){
System.out.println("Display by base : " + s);
}
public T getT(){
return (T)("t from base");
}
public void test(T t){ }
}
class GenericSub extends GenericBase{
public void display(String s){
System.out.println("Display by sub : " + s);
}
public String getT(){
return "t from sub";
}
/*
* test声明无法通过编译,因为与GenericBase原始类型的擦除后的test方法产生冲突
*/
// public void test(Object o){
// }
}
输出:
Display by sub : haha
t from sub
true
true
3. 约束与局限性
(1)运行时查询只适用于原始类型.
比如
List l = new ArrayList;
//可以通过编译
if(l instanceof List> ) {
...
}
//可以通过编译
if(l instanceof List) {
...
}
//编译出错
if (l instanceof List){
...
}
(2)不能抛出也不能捕获泛型类型实例
(3)不能声明参数化类型的数组,例如
List[] listArray = new List[10]; //编译出错
因为数组可以检查赋值操作的类型,List[]其原始类型为List[],如果声明可以成功,那么listArray = new List()也能成功,违背了数组检查类型的原则。
(4)不能实例化类型变量.
例如在GenericSample的方法中使用
T t = new T(); //编译出错
T t = T.class.newInstance(); //编译出错
要想实例化类型变量,需要用如下形式:
public T test( Class cls ) {
return cls.newInstance();
}
但是,对于一个泛型类型来说,如果其类型变量为T,其某个方法foo包含了泛型变量作为参数类型,那么及时将这个泛型类型对象a赋给其子类变量b(类型变量为? extends T),b也无法用T类型的参数调用foo,但它可以调用返回值为 ? extends T 类型的方法。如下
List x0 = new ArrayList();
x0.add("000");
List extends CharSequence> y0 = x0;
y0.add("0000"); //ERROR
CharSequence cs = y0.get(0); //OK
相反地,如果用超类限定符 ? super T ,那么可以调用T类型参数的方法,但是无法调用返回值为 ? super T的方法,如下
List x1 = new ArrayList();
x1.add("111");
List super String> y1 = x1;
y1.add("1111"); //OK
CharSequence cs = y1.get(0); //ERROR
String s = y1.get(0); //ERROR