论坛首页 Java企业应用论坛

Java语法糖的味道:泛型与类型擦除

浏览 19595 次
精华帖 (18) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-05-04  
支持 支持 很不错
0 请登录后投票
   发表时间:2011-05-04  
代码清单10-5中的重载当然不是根据返回值来确定的,之所以这次能编译和执行成功,是因为两个mehtod()方法加入了不同的返回值后才能共存在一个Class文件之中。第6章介绍Class文件方法表(method_info)的数据结构时曾经提到过,方法重载要求方法具备不同的特征签名,返回值并不包含在方法的特征签名之中,所以返回值不参与重载选择,但是在Class文件格式之中,只要描述符不是完全一致的两个方法就可以共存。

虽然道理是这样,但是我觉得还是不能解释  清单10-4和清单10-5之间的区别啊。

我怀疑这里可能还是编译器的工作原理问题。
  首先Lcy说的CLASS文件判断一个方法是否是同一个方法,确实和我们编写JAVA代码的时候不同。
我怀疑JAVAC编译的时候是可以是这样的
1:先会解析一些什么语法啊,词法,注解之类的。
2:把泛型的JAVA源代码先编译成一个中间的字节码形式。
3:把中间的字节码再转换成jvm的字节码(这个时候再进行泛型的擦除)。

对于ecj来说,可能就是
1:先会解析一些什么语法啊,词法,注解之类的。
2:对泛型先擦除泛型生成中间代码。
3:然后再进行把泛型中间代码生成字节码的时候发现存在两个相同的方法了。所以报错。

我觉得如果是这样也很好解释这种为什么在ECJ和JAVAC下面不同的原因。可能他们的在实现上对泛型的解析以及编译的时机可能不同,因为如果是JVM自动生成的字节码其实他真正表示一个方法就是Lay说的那样,返回值也会算在里面。
0 请登录后投票
   发表时间:2011-05-04  
希望lay和red都来解释一下我的那个想法是否正确,如果正确,希望给一个ecj或者javac 再编译JAVA源代码的时候执行机制的 说明就更好了。
0 请登录后投票
   发表时间:2011-05-04  
那个…… 如果方便的话,最好把名字叫对,HLLVM圈里面叫错了N次……可以叫icy,但Lcy或者lay我很囧啊。

ecj先不说,javac本身就是由java代码写的,有什么疑问其实debug一下就可以(源码在JDK_SRC_HOME/langtools/src/share/classes/com/sun/tools/javac中)。主要的动作在com.sun.tools.javac.main.JavaCompiler中的compile()和compile2()方法里。下面这张图可以帮助你阅读源码,至于整个javac工作的分析过程,回头我看看总篇幅多少,如果不大的话我找个时间发blog上吧。

  • 大小: 20.7 KB
0 请登录后投票
   发表时间:2011-05-04  
请教楼主一个问题
文章中说了对于泛型 c#采取的是膨胀法 Java采取的是擦除法
对于Java的擦除法 其实就是一个语法糖 编译后的class仍然采用的是强制类型转换

那么我们在Java编码中 如
   List list = new ArrayList();
   List<Integer> list = new ArrayList<Integer>();
使用泛型的好处 就在于 可以使得代码的语义更为明确 没有声明类型你不知道List里面存的具体是什么对象 声明泛型后可以让编译器为你检查 也使得阅读代码时能一眼就看出List里面存的具体是什么对象
  对于代码的性能,使用泛型与不使用泛型其实是一样的

不知道我这样的理解对不对
0 请登录后投票
   发表时间:2011-05-04  
引用
那个…… 如果方便的话,最好把名字叫对,HLLVM圈里面叫错了N次……


十分抱歉楼主。icy,icy,icy呵呵不会错了
0 请登录后投票
   发表时间:2011-05-04  
marmot 写道
请教楼主一个问题
文章中说了对于泛型 c#采取的是膨胀法 Java采取的是擦除法
对于Java的擦除法 其实就是一个语法糖 编译后的class仍然采用的是强制类型转换

那么我们在Java编码中 如
   List list = new ArrayList();
   List<Integer> list = new ArrayList<Integer>();
使用泛型的好处 就在于 可以使得代码的语义更为明确 没有声明类型你不知道List里面存的具体是什么对象 声明泛型后可以让编译器为你检查 也使得阅读代码时能一眼就看出List里面存的具体是什么对象
  对于代码的性能,使用泛型与不使用泛型其实是一样的

不知道我这样的理解对不对


仅在java中比较,你那两句的性能是一样的。毕竟上面一句你拿到list里面的元素,要用它还是要手动转型,下面的是编译器帮你插入转型代码而已,在代码角度没什么不同,最多就是在类的元数据中有点差别而已。

如果要比较java中和c#中的泛型性能,那就是很麻烦很难准确的事情,RednaxelaFX曾经写过一篇关于泛型的文章提到这点,你有兴趣可以找找看。

ps:文章中提到过我的观点,从性能角度去看泛型实在是没什么必要= =#
0 请登录后投票
   发表时间:2011-05-04  
IcyFenix 写道
marmot 写道
请教楼主一个问题
文章中说了对于泛型 c#采取的是膨胀法 Java采取的是擦除法
对于Java的擦除法 其实就是一个语法糖 编译后的class仍然采用的是强制类型转换

那么我们在Java编码中 如
   List list = new ArrayList();
   List<Integer> list = new ArrayList<Integer>();
使用泛型的好处 就在于 可以使得代码的语义更为明确 没有声明类型你不知道List里面存的具体是什么对象 声明泛型后可以让编译器为你检查 也使得阅读代码时能一眼就看出List里面存的具体是什么对象
  对于代码的性能,使用泛型与不使用泛型其实是一样的

不知道我这样的理解对不对


仅在java中比较,你那两句的性能是一样的。毕竟上面一句你拿到list里面的元素,要用它还是要手动转型,下面的是编译器帮你插入转型代码而已,在代码角度没什么不同,最多就是在类的元数据中有点差别而已。

如果要比较java中和c#中的泛型性能,那就是很麻烦很难准确的事情,RednaxelaFX曾经写过一篇关于泛型的文章提到这点,你有兴趣可以找找看。

ps:文章中提到过我的观点,从性能角度去看泛型实在是没什么必要= =#


谢谢icy的回答 我并不想比较c#与java两者之间对于泛型的性能
我只是 在Java代码中发现有的代码 使用了泛型声明 有的没有 我想知道这两者之间的差异
我的理解是 既然采取的是擦除法 那么在Java代码中 使用泛型与不使用泛型 除了在阅读代码时语义更为清晰之外
两者在性能上是一样的
不知道这样的理解对不对
0 请登录后投票
   发表时间:2011-05-04  
恩,同意楼上的说法
0 请登录后投票
   发表时间:2011-05-04  
引用
谢谢icy的回答 我并不想比较c#与java两者之间对于泛型的性能
我只是 在Java代码中发现有的代码 使用了泛型声明 有的没有 我想知道这两者之间的差异
我的理解是 既然采取的是擦除法 那么在Java代码中 使用泛型与不使用泛型 除了在阅读代码时语义更为清晰之外
两者在性能上是一样的
不知道这样的理解对不对



你理解是对的,只不过还有就是对如果你用IDE的话,那么这种开发速度也得到大大的提高。
import java.util.ArrayList;
import java.util.List;
public class Test {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("1");
		list.add("2");
		list.add("3");
		for (String str : list) {
			System.out.println(str);
		}
		System.out.println("over");
	}
}


如for循环里面一样,如果是从数据库取出来的值,那么你就可以直接用foreach了,连类型转换都省略了。

下面是for循环里面的汇编代码


L4 (20)
LINENUMBER 16 L4
ALOAD 1
INVOKEINTERFACE java/util/List.iterator()Ljava/util/Iterator;  //这里其实调用的是集合的iterator
ASTORE 3
GOTO L5
L6 (25)
ALOAD 3
INVOKEINTERFACE java/util/Iterator.next()Ljava/lang/Object; //  因为泛型被擦除了,所以List里面只能方Object

CHECKCAST java/lang/String  //这里是类型转换,因为泛型被擦除了,所以List里面只能放Object,所以要类型转换成String
ASTORE 2
L7 (30)
LINENUMBER 17 L7
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 2
INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
L5 (34)
LINENUMBER 16 L5
ALOAD 3
INVOKEINTERFACE java/util/Iterator.hasNext()Z
IFNE L6



所以以上代码其实和这个相同
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
			String str = (String) iterator.next();
			System.out.println(str);

		}
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics