由一点问题出错提醒
public class X {
void set(ArrayList<String> y) {
}
void set(ArrayList<Integer> y) {
}
}
才理解这就是所谓 type erasure (类型擦除) ,ArrayList<String>,ArrayList<Integer> 在 字节码文件 (1.5 会在签名部分有不同,可以反射获取泛型具体类型,但jvm运行没影响),jvm中都是同一类型。
1. 不同于 c++ 中的 template ,生成多个类 ,java 中仍是一个类 ,只不过生成字节码文件,替换 参数类型为指定上限(默认为Object),编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除, Java 语言中的泛型不能接受基本类型作为类型参数――它只能接受引用类型。
2. 所以 在 泛型类中 , 调用 参数类型相关的操作没有意义 (运行时已被擦为 上限), 不能调用 instanceof T and new T ( new 只能通过插入class然后反射来实现 ). 类型参数的作用域是定义这个类型参数的整个类,但是不包括静态成员函数。这是因为当访问同一个静态成员函数时,同一个类的不同实例可能有不同的类型参数,所以上述提到的那个作用域不应该包括这些静态函数,否则就会引起混乱。
3.泛型不具有协变性 ,X<Number> x<Integer> 没有上下级关系,协变性不能通过
import java.util.ArrayList;
public class template {
public static void main(String[] args) {
C<Number> x=new C<Number>();
C<Integer> y=new C<Integer>();
//error
x.get(y);
}
}
class C<T> {
public void get(C<T> t){
}
}
3.1 可以通过有界通配符实现
import java.util.ArrayList;
public class template {
public static void main(String[] args) {
C<Number> x=new C<Number>();
C<Integer> y=new C<Integer>();
x.get(y);
}
}
class C<T> {
public void get(C<? extends T> t){
}
}
有界通配符最适合用于 API,而不是客户机代码。
仅从某个结构中获取值时使用 extends 通配符;仅将值放入某个结构时使用 super 通配符;同时执行以上两种操作时不要使用通配符。
通配符不能出现在 class C<? extends x> 中 ,类需要确切类型参数名字,class C<T extends X> 使得 可以在类中调用 属于 X 的方法。
class C<T extends Number> {
public void get(C<? extends T> t){
}
public void get2(T t){
t.byteValue();
}
}
3.2 或者受限类型参数:
import java.util.ArrayList;
public class template {
public static void main(String[] args) {
C<Number> x=new C<Number>();
C<Integer> y=new C<Integer>();
x.get(y);
}
}
class C<T> {
public <X extends T> void get(C<X> t){
}
}
特点:(From 多角度看 Java 中的泛型 )
* 类型安全。 泛型的一个主要目标就是提高 Java 程序的类型安全。使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果没有泛型,那么类型的安全性主要由程序员来把握,这显然不如带有泛型的程序安全性高。
* 消除强制类型转换。泛型可以消除源代码中的许多强制类型转换,这样可以使代码更加可读,并减少出错的机会。
* 向后兼容。支持泛型的 Java 编译器(例如 JDK5.0 中的 Javac)可以用来编译经过泛型扩充的 Java 程序(GJ 程序),但是现有的没有使用泛型扩充的 Java 程序仍然可以用这些编译器来编译。
* 层次清晰,恪守规范。无论被编译的源程序是否使用泛型扩充,编译生成的字节码均可被虚拟机接受并执行。也就是说不管编译器的输入是 GJ 程序,还是一般的 Java 程序,经过编译后的字节码都严格遵循《Java 虚拟机规范》中对字节码的要求。可见,泛型主要是在编译器层面实现的,它对于 Java 虚拟机是透明的。
* 性能收益。目前来讲,用 GJ 编写的代码和一般的 Java 代码在效率上是非常接近的。 但是由于泛型会给 Java 编译器和虚拟机带来更多的类型信息,因此利用这些信息对 Java 程序做进一步优化将成为可能。
总之:为了避免JVM大的变动,java选择了在字节码级别保持向后兼容,而编译器则仅仅负责了检查以及自动添加强制转换代码来生成能兼容以前jvm的设计。
参考: