Java的泛型是伪泛型,Java在编译期间,所有的泛型信息都会被擦掉。正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
在调用泛型方法时,可以指定泛型,也可以不指定泛型。
- 在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一父类的最小级,直到Object
- 在指定泛型的情况下,该方法的几种类型必须是该泛型的实例的类型或者其子类
先检查类型,然后在进行类型擦除,再编译,以及编译的对象和引用传递问题
涉及类型检查的是它的引用。
ArrayList<String> list1 = new ArrayList(); list1.add("1"); //编译通过 list1.add(1); //编译错误 ArrayList list2 = new ArrayList<String>(); list2.add("1"); //编译通过 list2.add(1); //编译通过
自动类型转换
因为类型擦除的问题,所以所有的泛型类型变量最后都会被替换为原始类型。
既然都被替换为原始类型,那么为什么我们在获取的时候,不需要进行强制类型转换呢?
public E get(int index) {
RangeCheck(index);
return (E) elementData[index]; //(E)虽然泛型信息会被擦除掉,但是会将(E) //elementData[index],编译为(Date)elementData[index]
}
当存取一个泛型域时也会自动插入强制类型转换。自动地在字节码中插入强制类型转换。
运行时类型查询
在编译前的类型检查时报错误。
ArrayList<String> arrayList = new ArrayList<String>();
if(arrayList instanceof ArrayList<String>){
}
因为类型擦除之后,ArrayList<String>
只剩下原始类型ArrayList
,泛型信息String
不存在了。
不会报错
if(arrayList instanceof ArrayList){
}
泛型在静态方法和静态类中的问题
泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数
举例说明:
public class Test2<T> {
public static T one; //编译错误
public static T show(T one){ //编译错误
return null;
}
}
因为泛型类中的泛型参数的实例化是在定义对象的时候指定的,而静态变量和静态方法不需要使用对象来调用。对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的。static方法要使用泛型能力,就必须使其成为泛型方法。
但是要注意区分下面的一种情况:
public class Test2<T> {
public static <T >T show(T one){ //这是正确的。 在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。static方法要使用泛型能力,就必须使其成为泛型方法。
return null;
}
}
类型擦除与多态的冲突和解决方法 --(桥方法)
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
//编译器生成的类。
class MyNode extends Node {
// 编译器生成的桥方法
//
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}
泛型方法与可变参数
再看一个泛型方法和可变参数的例子:
public <T> void printMsg( T... args){ for(T t : args){ Log.d("泛型测试","t is " + t); } }
printMsg("111",222,"aaaa","2323.4",55.55);
java 和C++泛型的区别
在C++中,模板本质上就是一套宏指令集,只是换了个名头,编译器会针对每种类型创建一份模板代码的副本。
有个证据可以证明这一点:MyClass<Foo>不会与MyClass<Bar>共享静态变量。然而,两个MyClass<Foo>实例则会共享静态变量
由于架构设计上的差异,Java泛型和C++模板还有如下很多不同点;
1. C++模板可以使用int等基本数据类型。Java则不行,必须转而使用Integer
2. Java中,可以将模板的类型参数限定为某种特定类型。例如,你可能会使用泛型实现CardDeck,并规定参数必须扩展自CardGame。
3. C++中,类型参数可以实例化,Java不可以实例化
4. Java中,类型参数(即MyClass<Foo>中的Foo)不能用于静态方法和变量,因为他们会被MyClass<Foo>和MyClass<Bar>共享。但在C++中,这些类是不同的,类型参数可以用于静态方法和静态变量。
5. 在Java中,不管类型参数是什么,MyClass的所有实例都是同一类型。类型参数会在运行时被抹去。而C++中,参数类型不同,实例类型也不同
参考:
https://www.cnblogs.com/wuqinglong/p/9456193.html
https://www.cnblogs.com/linghu-java/p/10031239.html
https://blog.csdn.net/coding_is_fun/article/details/81564512