关于泛型和重载的小问题

 首先描述问题,看代码。

代码例子:

view plaincopy to clipboardprint?
import java.util.ArrayList;  
public class Test {  
    public static String test(ArrayList list){  
        return "";  
    }  
 
    public static Integer test(ArrayList list){  
        return 1;  
    }  
 
    public static void main(String[] args) {  
        ArrayList listStrs = new ArrayList();  
        ArrayList listInts = new 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 list){
        return "";
    }

    public static Integer test(ArrayList list){
        return 1;
    }

    public static void main(String[] args) {
        ArrayList listStrs = new ArrayList();
        ArrayList listInts = new 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 list)和test(ArrayList list),在java语言的级别,这是合法的重载,因为编译器可以通过参数类型信息来确定调用哪个版本。再加上返回类型不同,经过编译和类型擦除得到的两个方法是可以在class文件中共存的。这样问题就解决了。

比如我们写两个方法,

 public void test(ArrayList list){}
 public void test(ArrayList list){}

好,这两个方法是合法的重载吧。从语言角度讲,我们可以说是。但是,经过擦除之后,这两个方法就是完全相同的了,如果编译器支持这种情况,那生成的class文件就是jvm不支持的了。那咋办?编译拒绝通过


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ZangXT/archive/2009/09/13/4547388.aspx

你可能感兴趣的:(CoreJava)