泛型类
一个泛型类 (generic class) 就是具有一个或多个类型变量的类。
- 泛型类可以有多个类型变量。 例如 , 可以定义 Pair 类 , 其中第一个域和第二个域使用不同的类型,类定义中的类型变量指定方法的返回类型以及域和局部变量的类型。
public class a<T,U>{}
泛型方法
当某个类只有一个方法需要使用泛型时,可不必将整个类声明为泛型类,可以单独声明泛型方法。
- 泛型方法可以定义在普通类中, 也可以定义在泛型类中。
- 当调用一个泛型方法时,在方法名前的尖括号中放人具体的类型。
public class ArrayAlg{
public static <T> T getMiddle(T... a){
return a[a.length / 2];
}
}
String middle = ArrayAlg.<String>getMiddle("a","b","c");
//等同于,编译器有足够的信息能够推断出所调用的方法
String middle = ArrayAlg.getMiddle("a","b","c")
类型变量的限定
限定表示对变量进行进一步限制,必须继承自某类或者实现某接口。
- <T extends BoundingType〉示 T 应该是绑定类型的子类型 (subtype) 。T 和绑定类型可以是类, 也可以是接口 。 选择关键字 extends 的原因是更接近子类的概念。
- 一个类型变量或通配符可以有多个限定,限定类型用 “ & ” 分隔, 而逗号用来分隔类型变量,有个限定多个接口,但同时只能限定一个类,如果包含类时,需要将类作为第一位。
public static <A extends Comparable,B extends Comparable> void test(A a,B b){}
类型擦除
无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type) 。原始类型的名字就是删去类型参数后的泛型类型名。 擦除类型变M , 并替换为限定类型(无限定的变量用Object)。
public class A<T>{
private T a;
private T b;
}
//由于T是“无限定变量”擦除后将转变成原始类型(Object)
public class B{
private Object a;
private Object b;
}
- 原始类型用第一个限定的类型变量来替换 , 如果没有给定限定就用 Object 替换。
public class A<T extends Comparable & Serializable> implements Serializable{
private T a;
private T b;
}
//将转化为
public class A implements Serializable{
private Comparable a;
private Comparable b;
}
- 当程序调用泛型方法时, 如果擦除返回类型 ,编译器插入强制类型转换。
Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
//编译器把这个方法调用翻译为两条虚拟机指令
//对原始方法 Pair.getFirst()的调用,此时泛型方法将返回Object
//将返回的Object类型强制转换为Employee类型。
虚拟机对当get或set一个泛型字段时也会插人强制类型转换。
-
桥方法
在继承泛型类型的时候,桥方法的合成是为了避免类型变量擦除所带来的多态灾难。
public class a<T>{
private T name;
public void setName(T name){}
public T getName(){}
}
//类型擦除后转变为
public class a{
private Object name;
public void setName(Object name){}
public Object getName(){}
}
public class b extends a<String>{
public void setName(String name){}
public String getName(){}
}
//类型擦除后转变为 重载了setName方法而不是重写
public class b extends a{
public void setName(String name){}
//编译器会生成一个桥方法来实现多态
public void setName(Object name){
setName(String(name));
}
//编译器会生成一个桥方法实现对getName的覆盖
public Object getName(){
return String(this.name);
}
}
-
虚拟机中没有泛型 , 只有普通的类和方法 。
所有的类型参数都用它们的限定类型替换 。
桥方法被合成来保持多态 。
为保持类型安全性 , 必要时插人强制类型转换