首先描述问题,看代码。
代码例子:
view plaincopy to clipboardprint?
import java.util.ArrayList;
public class Test {
public static String test(ArrayList
return "";
}
public static Integer test(ArrayList
return 1;
}
public static void main(String[] args) {
ArrayList
ArrayList
System.out.println(test(listStrs));
System.out.println(test(listInts));
/*
* 异常测试
ArrayList list = new ArrayList();
无法确定重载版本
test(list);
*/
}
}
import java.util.ArrayList;
public class Test {
public static String test(ArrayList
return "";
}
public static Integer test(ArrayList
return 1;
}
public static void main(String[] args) {
ArrayList
ArrayList
System.out.println(test(listStrs));
System.out.println(test(listInts));
/*
* 异常测试
ArrayList list = new ArrayList();
无法确定重载版本
test(list);
*/
}
}
有人关心,既然java的泛型是采用的擦除技术,那代码中的两个test方法,就是签名完全相同的了,为什么会允许这种情况存在?
的确,经过编译,泛型相关的类型信息擦除了,我们可以直接通过查看字节码来确定两个方法的签名和类型。为了方便易懂,可以直接使用jad进行反编译,得到的代码是:
public static String test(ArrayList arraylist)
{
return "";
}
public static Integer test(ArrayList arraylist)
{
return Integer.valueOf(1);
}
我们再把反编译的代码进行编译,显然是无法通过的,因为编译器不允许上面这个两个方法同时存在。
混乱吧?的确。问题出在哪里?根本问题是我们对讨论的范围不明确,没有注意java语言和jvm之间的边界。具体的来说,我们说的泛型、重载是java语言级别的,但“擦除”技术是关于实现的,它关系到合法class文件的生成,而合法的class文件才能被jvm接受。
javac编译器到底采用什么技术实现泛型,jvm是否需要做相应修改,我们可以不管,我们只要知道什么是合法的java程序就够了。
再回来看我们的问题。
首先,jvm本来就支持签名相同,但返回类型不同的方法存在。参考《深入java虚拟机》第二版中文版的6.6节:
“在java源文件的同一个类里,如果声明了两个相同名字和相同参数类型、但返回值不同的方法,这个程序将无法编译通过。在java程序设计语言中,不能仅仅通过返回值的不同来重载方法。但是这样的两个方法可以和谐地在一个class文件中共存。”
在java语言角度的添加这种限制也是自然的。比如两个方法:
void test(int i);
int test(int i);
如果使用int a = test(i);编译器还可能通过返回类型来确定这里调用的是int test(int i);那如果直接调用test(i)呢?鬼才知道该选择哪一个。
其次,对于例子中的两个方法test(ArrayList
比如我们写两个方法,
public void test(ArrayList
public void test(ArrayList
好,这两个方法是合法的重载吧。从语言角度讲,我们可以说是。但是,经过擦除之后,这两个方法就是完全相同的了,如果编译器支持这种情况,那生成的class文件就是jvm不支持的了。那咋办?编译拒绝通过
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ZangXT/archive/2009/09/13/4547388.aspx