返回类型是擦除的一部分吗?

时间:2010-11-22 19:38:49

标签: java generics

有人可以解释为什么第二类不能编译吗?

1使用javac和JDK 6编译好(Eclipse会抱怨此代码)

public class SameSignatureMethods {
    public <T extends String> Boolean test()
    {
        return true;
    }

    public <T extends Character> Double test() 
    {
        return 1d;
    }
}

2对该示例稍作修改,编译失败并出现以下错误:

name clash: <T>test() and <T>test() have the same erasure

唯一的变化是方法的返回类型:

public class SameSignatureMethods {
    public <T extends String> Boolean test()
    {
        return true;
    }

    public <T extends Character> Boolean test() {
        return true;
    }
}

这就是第一类主要方法的样子:

public static void main(String[] args) {
    SameSignatureMethods m = new SameSignatureMethods();
    System.out.println("m.<Character>test()=" + m.<Character>test());
    System.out.println("m.<String>test()=" + m.<String>test());
}

3 个答案:

答案 0 :(得分:4)

因此,JDK编译器编译第一个版本但不编译第二个版本,而Eclipse编译器编译两个版本中的任何一个版本。

从Java字节代码的角度来看,第一个版本包含两种不同的方法(在类型擦除之后),即public java.lang.Boolean test()public java.lang.Double test(),这是完全有效的。当您覆盖泛型方法时,JDK编译器和Eclipse编译器有时会生成此类方法,但这些方法会被标记为合成桥接方法。

第二个版本将包含两个具有相同签名的方法(在类型擦除之后),这在Java字节代码中是不允许的。因此JDK编译器无法生成这样的类文件。我刚刚使用十六进制编辑器编辑了一个类文件来创建一个具有这些方法的类,并且在启动程序时,我收到此错误:

Exception in thread "main" java.lang.ClassFormatError: Duplicate method name&signature in class file SameSignatureMethods
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$000(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: SameSignatureMethods.  Program will exit.

我开始的课程看起来像这样。我使用String和Double因为它们具有相同的名称长度:

public class SameSignatureMethods {
    public  <T extends String> String test() {
        return null;
    }

    public  <T extends Double> Double test() {
        return null;
    }

    public static void main(String[] args) {
        System.out.println(new SameSignatureMethods().<Double>test());
    }
}

然后,使用十六进制编辑器,我将第一个方法的签名更改为public <T extends String> Double test(),在类文件的两个位置,一个具有原始签名()Ljava/lang/Double;,一个具有通用签名{ {1}}。

答案 1 :(得分:2)

听起来你已经成功地混淆了你的编译器:

  • 返回类型不是签名的一部分。编译器不能使用返回类型来告诉调用哪个方法。

  • 在您的示例中,无论如何,塞入方法签名的通用内容都不会影响返回类型。

  • 同样说<T extends String>毫无意义,你无法扩展最终类型。 (嗯,这只是一个警告,它不会停止编译)

你想知道为什么第二个类不编译,我想知道为什么第一个类编译。 按原样,第一类编译,尽管有警告。取出尖括号的东西会导致出现“重复方法”错误,无论如何都应该出现。必须是编译器错误。

答案 2 :(得分:1)

在第一个类(SameSignatureMethods)中,运行时的2个方法分别返回BooleanDouble。在第二类中,方法都返回Boolean

<T extends String>在方法定义前面并不意味着它是一个返回类型。

也许你想做这样的事情:

public <T extends String> T test()
{
    return obj;// obj could be a String
}

但是,泛型类型会在运行时被删除,上面的方法将变为

public Object test()
{
    return obj;// obj could be a String
}