上一篇(泛型(一))介绍了一下各种定义,包括什么是泛型、泛型的优点、泛型擦除、泛型类、泛型接口、泛型方法、泛型通配符以及泛型上下边界,这一篇主要是上一篇的延续和补充
限定类型变量
在上一篇中,我们知道在使用泛型的时候,可以通过extends 限制泛型实参的上边界,除了限制上边界,我们还可以对泛型的类型变量加以约束,看下例子:
class ExampleClass8{
//计算最小值
public <T extends Comparable> T min(T paramA, T paramB){
return paramA.compareTo(paramB)>0? paramB: paramA;
}
}
为了确保传入方法的两个参数都具有compareTo方法,这里可以限制T 实现了Comparable接口。在使用的时候,如果传入了的泛型实参没有实现Comparable接口,就会报错。
还有一点需要注意的是,extends前面可以放置多个泛型,extends之后如果有多个类型的话,类型之间用&符号隔开,而且如果有类类型只能有一个类类型,第一个必须是类类型,其余的是接口类型
//第一个必须是类类型,这个会报错
public <T extends Comparable & Apple> T min(T paramA, T paramB){...}
//正确
public <T extends Apple & Comparable> T min(T paramA, T paramB){...}
//有类类型只能有一个类类型,报错
public <T extends Apple & Orange &Comparable> T min(T paramA, T paramB){...}
//正确
public <T extends Apple& TestInterface1 & Comparable> T min(T paramA, T paramB){...}
另外,这种限定既可以用在泛型方法上也可以用在泛型类上。泛型类上的使用就不举例了。
泛型类型的继承关系
泛型类可以继承或实现(扩展)其它泛型类,比如ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{...}
如果两个类存在继承关系,如果在泛型类中使用这两个类作为泛型实参,那这两个泛型类是不存在继承关系的。
例子如下
public class ExampleUnitTest {
@Test
public void test() {
ExampleClass9<Fruit> fruit = new ExampleClass9<Apple>();
}
}
class ExampleClass9<T> {
public void eat(ExampleClass9<Fruit> fruit){
}
}
public class Fruit {
private String name;
public Fruit() {
}
public Fruit(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Apple extends Fruit {
public Apple() {
}
public Apple(String name) {
super(name);
}
@Override
public String toString() {
return "Apple{}";
}
}
由于ExampleClass9< Fruit>与ExampleClass9< Apple>是没有关系的两个类,如果在一个方法中同时传入这两种参数是行不通的,但是正常不使用泛型类的情况下是可以向上转型的(多态),
为了解决这种问题,引入了泛型通配符的概念,这个可以看一下上一篇。
泛型中的约束与局限性
1.泛型参数不能用基本数据类型
2.运行时类型查询只适用于原生类型
泛型类中类的泛型不论传的时什么类型,getClass获取都是原生类的类型,获取的类名都是一样的,且泛型类不能使用instanceof 判断类的类型
打印
3.在泛型类中,不能在静态域或方法中使用泛型类中的泛型,除非是一个静态的泛型方法
Java虚拟机是如何实现泛型的
泛型是JDK 5.0的新特性,在此之前是没有泛型的概念的,这时的Java只能通过Object是所有类型的父类和强制类型转换两个特点来实现类型泛化。
由于java.lang.Object是所有Java类的父类,所以Object转型成为任何对象都是有可能的。这个特点,它是有缺陷的,因为只有程序员和运行期的虚拟机才知道Object到底是什么类型的对象,程序编译期间,编译器不能检查Object是否能够强转成功,程序都是人写的,不能保证每次强转的正确性,造成许多类型转换异常(ClassCastException)的风险发生在运行期。
Java引入的泛型为了兼容JDK5.0之前的代码并不是一个真正意义上的泛型,实际上是Java语言的一个语法糖,它只在程序源码中存在,在编译后的字节码文件中,被替换为原来的原生类型(Raw Type,也称为裸类型),并在相应的地方插入了强制转换代码,这个也被成为泛型擦除。因此对于运行期来说ArrayList< Integer>于ArrayList< Double>其实就是同一个类。为了在泛型类中获取传入的参数化类型,虚拟机规范中添加了Signature、LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题。Signature的作用是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。
泛型擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还保留了泛型信息,这也是我们能通过反射手段获得参数化类型的根本依据。