java泛型擦除原因,何为迁移兼容性

看了thinking in java 里的泛型篇,搞不懂里面讲的泛型的核心动机是迁移兼容性,为了使泛化客户端能够使用非泛化库类,反之亦然。
作者分别了向后兼容性和迁移兼容性,可是没用例子说明什么样是迁移兼容性,能举个例子说明一下如果泛型不擦除,就无法迁移代码吗?十分感谢!

3个回答

1、你要理解java编译和解释执行的过程。在各过程中,范型的存在性、存在方式、被处理方式。
2、编译器编译源代码时,会检查范型,防止类型出错。
例如:List usernames = new ArrayList();将不能通过编译。
它将源代码编译为字节码,这时候的字节码,与没有使用范型编译过来的字节码比较,多出了一些范型信息。
但是这些范型信息的有无,对于JVM是一样的(如果不考虑反射)。
List usernames = new ArrayList();
Object o = usernames;
List ints = (List)o;//warning
usernames.add("avril lavigne");
ints.add(100);
以上代码,ints和usernames指向同一个列表,但是却可以添加字符串和整型对象。
这段代码应该会加深你对范型的理解。
3、 对“拭去法”的理解,并不只是简单的将范型信息擦出。
public class Hello {
T t;
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Type[] types = Hello.class.getTypeParameters();
System.out.println(types[0]);
Hello h = new Hello<>();
h.t = "";
h.getClass().getDeclaredField("t").set(h, 1);;
System.out.println(h.t);
}
}
这段代码通过编译,并输出
T
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at generic.Hello.main(Hello.java:13)
也就是说,字节码被载入jvm后,字段的类型仍然保持为T。这也验证了范型是参数化类型的说法。
如果再加两行代码
Hello h2 = new Hello<>();
h2.t = (Integer)(Object)h.t;
也将通过编译,然而运行却报异常。
说明在实例化对象的时候,动态的将字段的类型进行了设定,和的信息对运行时产生了影响!!!
同一个类型实例化的对象,相同的属性类型却不能匹配!!!
有范型和没范型,编译得到的字节码有本质的不同。jvm载入后,类中含有参数化类型。
这个类型化参数,是在运行时动态绑定的!
范型没有被拭去?是的。"拭去法"并不能简单的理解。
更准确的理解应该是,编译时,对于类定义,范型作为类型的参数化信息,并不决定类型。
拭去,指的是在类定义时,范型不决定被定义类的类型,而不是指范型信息被擦除!范型信息仍然是该类的一部分!!!

主要是为了代码在jvm层面上获得兼容性。实现泛型,我们可以在java语言层面做(类型擦除),或者在java虚拟机层面做(实际上C#的泛型就是在.NET虚拟机上做的),java并不是jvm上唯一的编程语言,并不是所有的语言都支持泛型,如果在jvm上做,那么就意味着早期的jvm字节程序,或者用不兼容泛型语法的语言写的程序就和这个版本的jvm不兼容了。
这也是为什么.net framework 2/3 完全不兼容 .net 1.x 的原因。

qq_35242008
qq_35242008 我概括一下为什么我认为不擦除也行的原因吧,您能帮我指下哪里理解错了好吗? 以容器类为例 ①如果容器类改成泛型,因为旧代码没有指定泛型参数,所以规定在编译时,若不指定参数,编译成Object型的字节码,而它对jvm加载类时,加载jvm处理好的容器类重载类型Class对象。 ②如果用泛型容器指定参数类型,编译后保留参数类型,相当于我使用的始末都是List<String>,编译器和jvm的功能就是帮我"重载了类"帮助加载。 我以为这就是c++的做法,不知道哪有错。。。
3 年多之前 回复
qq_35242008
qq_35242008 谢谢回答!不好意思我就是对那个编译器一定要擦除搞不懂。 您说的早期的字节程序,是Object型的。我的理解是,就算我现在擦除了,对于原来的旧代码,应该是没影响的啊。以容器类为例,只要规定:非泛型容器编译后是Object型,泛型容器若不指定参数类型T,即当做非泛型用,那就编译成Object型,所以即使非泛型库类换成泛型的了,对旧代码没影响额。(①我是不是这里理解有问题,因为如果我现在只有泛型容器类,我用泛型参数<String>,要是不擦除而保留参数类型,jvm没法找到加载String类的泛型容器??②能不能就算擦除了,jvm帮助做幕后工作,将Class对象也分参数类型加载?这在别的语言像c++可以做到吗,因为书上说c++的泛型能保留参数) 您说的别的语言不用泛型在jvm上,我觉得只要规定不指定泛型参数类型而使用泛型,一律当做Object型就可以了。 所以,其实我还是不懂它为什么硬要擦除,问了好多人都没个准,希望您能教下,谢谢!
3 年多之前 回复
   首先,泛型是Java的糖衣语法,因为容器类型必须保证其中的元素类型都一致,指定泛型可以让开发工具先检验元素类型是否一致;其次,糖衣语法被编译后其实就没有类型了,即泛型擦除,jdk是从1.5之后才添加泛型功能的。只能1.5以后的版本向后兼容的。最后,泛型有利于开发者,指定类型后,可以直接用,不需要作类型转换的。
qq_35242008
qq_35242008 抱歉,,我还是不知道为什么向后兼容一定要擦除,我觉得不擦除也能向后兼容,,您能看下下面一个评论里的回复吗,我把原因写那里了T_T竟然不能复制。能指一下我哪里理解错了吗,谢谢!
3 年多之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问