论坛首页 Java企业应用论坛

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

浏览 19594 次
精华帖 (18) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-05-04  
xgj1988 写道
代码清单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说的那样,返回值也会算在里面。

正解:在Class文件格式之中,只要描述符不是完全一致的两个方法就可以共存
0 请登录后投票
   发表时间:2011-05-04  
刚才测了一下在eclipse使用如下代码代码。发现在ecj编译的时候并不会出错。对于我的猜测我表示抱歉。没有实验就发表了。

package org.test.generic;

import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author xgj
 * 
 */
public class Test {
	//	public void fn1(List<String> l1) {
	//	}
	//
	//	public void fn1(List<Integer> l1) {
	//	}

	public static String fn1(List<String> l1) {
		System.out.println("l1");
		return "0";
	}

	public static int fn1(List<Integer> l1) {
		System.out.println("l2");
		return 0;
	}

	public static void main(String[] args) {
		fn1(new ArrayList<String>());
		fn1(new ArrayList<Integer>());
	}
}


那么应该ECJ和JAVAC在处理这一块的时候都是一样的
0 请登录后投票
   发表时间:2011-05-04  
如果把icy的代码改下,会得到一个奇怪的问题
public class Test {
	//	public void fn1(List<String> l1) {
	//	}
	//
	//	public void fn1(List<Integer> l1) {
	//	}

	public static String fn1(List l1) {
		System.out.println("l1");
		return "0";
	}

	public static int fn1(List<Integer> l1) {
		System.out.println("l2");
		return 0;
	}

	public static void main(String[] args) {
		fn1(null);//调用的是 public static int fn1(List<Integer> l1)
//		fn1(new ArrayList<Integer>());
	}	
}


但是如果这样,就让编译器找不到如何下手了。
/**
 * 
 * @author xgj
 * 
 */
public class Test {
	//	public void fn1(List<String> l1) {
	//	}
	//
	//	public void fn1(List<Integer> l1) {
	//	}

	public static String fn1(List<String> l1) {
		System.out.println("l1");
		return "0";
	}

	public static int fn1(List<Integer> l1) {
		System.out.println("l2");
		return 0;
	}

	public static void main(String[] args) {
		fn1(null);
//		fn1(new ArrayList<Integer>());
	}	
}


而且对于第一种情况,在JAVAC下面无法编译,在ECJ下面可以编译。但是如果是第二种情况,JAVAC和ECJ都出现同样的问题。、

reference to fn1 is ambiguous, both method fn1(java.util.List<java.lang.String>) in org.test.generic.Test and method fn1(java.util.List<java.lang.Integer>) in org.test.generic.Test match
fn1(null);
0 请登录后投票
   发表时间:2011-05-04  
有点费解。
0 请登录后投票
   发表时间:2011-05-04  

谢谢icy和xgj的回复 很感谢
icy说可以参考 RednaxelaFX 的贴  我找到以下两个帖子


主题:java和C#的泛型比较
http://www.iteye.com/topic/348434


主题:Object数组到泛型数组转换的伪解决方案
http://www.iteye.com/topic/320161

 

帖子很长 说的很也很好 我还得消化消化 呵呵

0 请登录后投票
   发表时间:2011-05-04  
xgj1988 写道
如果把icy的代码改下,会得到一个奇怪的问题
public class Test {
	//	public void fn1(List<String> l1) {
	//	}
	//
	//	public void fn1(List<Integer> l1) {
	//	}

	public static String fn1(List l1) {
		System.out.println("l1");
		return "0";
	}

	public static int fn1(List<Integer> l1) {
		System.out.println("l2");
		return 0;
	}

	public static void main(String[] args) {
		fn1(null);//调用的是 public static int fn1(List<Integer> l1)
//		fn1(new ArrayList<Integer>());
	}	
}


但是如果这样,就让编译器找不到如何下手了。
/**
 * 
 * @author xgj
 * 
 */
public class Test {
	//	public void fn1(List<String> l1) {
	//	}
	//
	//	public void fn1(List<Integer> l1) {
	//	}

	public static String fn1(List<String> l1) {
		System.out.println("l1");
		return "0";
	}

	public static int fn1(List<Integer> l1) {
		System.out.println("l2");
		return 0;
	}

	public static void main(String[] args) {
		fn1(null);
//		fn1(new ArrayList<Integer>());
	}	
}


而且对于第一种情况,在JAVAC下面无法编译,在ECJ下面可以编译。但是如果是第二种情况,JAVAC和ECJ都出现同样的问题。、

reference to fn1 is ambiguous, both method fn1(java.util.List<java.lang.String>) in org.test.generic.Test and method fn1(java.util.List<java.lang.Integer>) in org.test.generic.Test match
fn1(null);


你这个例子和泛型已经没啥联系了,这个是方法静态选择的问题。和这段代码遇到的问题是一样的:
public class Foo {

	class Foo1 {

	}

	class Foo2 {

	}

	static void method(Foo1 foo) {

	}

	static void method(Foo2 foo) {

	}

	public static void main(String[] args) {
		method(null);
	}
}
关于方法选择的规则,请参考Java语言规范的§15.12.2.5 Choosing the Most Specific Method章节
0 请登录后投票
   发表时间:2011-05-05  
引用
关于方法选择的规则,请参考Java语言规范的§15.12.2.5 Choosing the Most Specific Method章节


额,呵呵。思想惯性了。谢谢icy
0 请登录后投票
   发表时间:2011-12-19  
xgj1988 写道
如果把icy的代码改下,会得到一个奇怪的问题
public class Test {
	//	public void fn1(List<String> l1) {
	//	}
	//
	//	public void fn1(List<Integer> l1) {
	//	}

	public static String fn1(List l1) {
		System.out.println("l1");
		return "0";
	}

	public static int fn1(List<Integer> l1) {
		System.out.println("l2");
		return 0;
	}

	public static void main(String[] args) {
		fn1(null);//调用的是 public static int fn1(List<Integer> l1)
//		fn1(new ArrayList<Integer>());
	}	
}


但是如果这样,就让编译器找不到如何下手了。
/**
 * 
 * @author xgj
 * 
 */
public class Test {
	//	public void fn1(List<String> l1) {
	//	}
	//
	//	public void fn1(List<Integer> l1) {
	//	}

	public static String fn1(List<String> l1) {
		System.out.println("l1");
		return "0";
	}

	public static int fn1(List<Integer> l1) {
		System.out.println("l2");
		return 0;
	}

	public static void main(String[] args) {
		fn1(null);
//		fn1(new ArrayList<Integer>());
	}	
}


而且对于第一种情况,在JAVAC下面无法编译,在ECJ下面可以编译。但是如果是第二种情况,JAVAC和ECJ都出现同样的问题。、

reference to fn1 is ambiguous, both method fn1(java.util.List<java.lang.String>) in org.test.generic.Test and method fn1(java.util.List<java.lang.Integer>) in org.test.generic.Test match
fn1(null);


这段代码在eclipse 3.4版本下无法编译通过。
提示如下:Method fn1(List<Integer>) has the same erasure fn1(List<E>) as another method in type Test1<T>

是eclipse3.4版本修正过了么?
0 请登录后投票
   发表时间:2012-04-11  
受益匪浅,感谢
0 请登录后投票
   发表时间:2012-04-12  
受益匪浅,谢谢,又加深了对泛型的理解。
0 请登录后投票
论坛首页 Java企业应用版

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