围绕“相同擦除”编译错误的奇怪行为

时间:2010-10-19 04:28:02

标签: java generics type-erasure

我最近偶然发现了一段代码,由于“相同的擦除”问题(看起来与this one非常相似)而无法在我的Eclipse中编译。编写代码的人向我保证,它会在当地环境中进行编译并持续集成,因此我一直在模仿它。

看一下这个片段:

package com.mycompany.playground;

import java.util.ArrayList;
import java.util.Collection;

public class GenericsTest {

    public static void main (String[] args) {
        System.out.println(GenericsTest.doSomething(new ArrayList<A>()));
        System.out.println(0 == GenericsTest.doSomething(new ArrayList<C>()));
    }

    public GenericsTest() {
    }

    public static String doSomething(Collection<A> listOfA) {
        return "has done something to Collection<A>";
    }

    public static Integer doSomething(Collection<C> listOfC) {
        return 0;
    }

    private class A {

    }

    private class C {

    }

}
使用1.6.0_21 JDK作为工作空间默认值的Eclipse Helios将无法编译它并且会抱怨Method doSomething(Collection)与GenericsTest类型中的另一种方法具有相同的擦除doSomething(Collection)。它会对另一种方法说同样的话。

尝试强制Eclipse运行它并看到:线程“main”中的异常java.lang.Error:未解决的编译问题:GenericsTest类型中的方法doSomething(Collection)不适用于参数(ArrayList)。 / p>

确定。这是可以预料的。现在。如果我进入我的命令行并运行简单:

javac GenericsTest.java

它编译。我检查了1.6.0_21和1.6.0_06(这些人在他们的环境中所拥有的那个)并没有抱怨。我将类文件复制到Eclipse预期的位置,并强制它再次运行它。

打印:

has done something to Collection<A>
true

如果我更换

System.out.println(0 == GenericsTest.doSomething(new ArrayList<C>()));

System.out.println(GenericsTest.doSomething(new ArrayList<C>()));

它仍会在没有来自命令行的警告的情况下进行编译,但在尝试运行时会给出相同的“未解决的编译问题”。

这里有两个问题。

  • javac是否比内置的Eclipse编译器更聪明?看起来几乎完全像this previously asked question所以我相信我知道答案。 (顺便说一句,我如何告诉Eclipse使用 javac ?)。

  • 为什么 javac 会以静默方式编译 java 然后无法运行的内容(删除了{0 ==}“提示”的第二种情况?

4 个答案:

答案 0 :(得分:2)

根据Java规范,应该通过签名(名称+参数类型)区分两种方法,而不是返回类型。由于JDK中的错误,可以编译原始代码 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6182950 在某些版本的Eclipse中已修复此错误,这就是为什么有些人无法在Eclipse中编译它。 至于编译代码实际可行的原因,您应该理解Java语言不等同于JVM字节代码。在字节代码中,您可以拥有大量非法Java名称,是的,字节代码通过签名,返回类型以及可能的其他信息来区分方法。

答案 1 :(得分:1)

这里的问题是: 每当您有两个具有相同名称的方法时,它们必须具有不同的签名。这里考虑的签名不包括返回类型,因此这两个方法不能在同一个类中声明

int foo(A a) 
float foo(A a)

在您的示例中,您有两个具有不同参数类型(Collection<A>Collection<C>)的不同方法,但在内部,当编译器发挥其魔力时,泛型会被视为Collection。这就是“相同的擦除”的意思。

我现在还不记得所有java版本都显示这种行为,因为我一直坚持使用java 5和java 6。

希望它有所帮助。

答案 2 :(得分:1)

帕维尔,我想我遇到了你所描述的同样问题。

今天我用自己的代码进行了攻击,并为Eclipse和Java的编译器提供了相同的不同行为。虽然,它似乎是一个已知的编译器问题。

请参阅Bug 6182950

谢谢!

答案 3 :(得分:0)

我的Eclipse中的两个代码片段(有和没有0 ==)都适合我。我正在使用Eclipse 3.3.2和JDK 1.6.0_21-b07,它被配置为Eclipse的“Alternative JRE”。可能这就是它对我有用的原因。