Java泛型代码使用javac编译,Eclipse Helios失败

Dee*_*ech 9 java eclipse generics javac

我有以下测试类,它使用泛型来重载方法.它在使用javac编译时有效,无法在Eclipse Helios中编译.我的java版本是1.6.0_21.

我读到的所有文章都表明Eclipse是对的,这段代码不适用.但是,使用javac和run编译时,会选择正确的方法.

这怎么可能?

谢谢!

import java.util.ArrayList;

public class Test {
    public static void main (String [] args) {
        Test t = new Test();
        ArrayList<String> ss = new ArrayList<String>();
        ss.add("hello");
        ss.add("world");
        ArrayList<Integer> is = new ArrayList<Integer>();
        is.add(1);
        is.add(2);
        System.out.println(t.getFirst(ss));
        System.out.println(t.getFirst(is));
    }   
    public String getFirst (ArrayList<String> ss) {
        return ss.get(0);
    }
    public Integer getFirst (ArrayList<Integer> ss) {
        return ss.get(0);
    }
}
Run Code Online (Sandbox Code Playgroud)

mer*_*ike 6

Java语言规范,第8.4.2节写道:

在类中声明具有覆盖等效签名(在下面定义)的两个方法是编译时错误.

如果m1是m2的子签名或m2是m1的子签名,则两个方法签名m1和m2是覆盖等价的.

方法m1的签名是方法m2的签名的子签名(如果有的话)

  • m2与m1具有相同的签名,或

  • m1的签名与m2的签名擦除相同.

显然,这些方法不是覆盖等价的,因为ArrayList<String>它不是ArrayList(擦除ArrayList<Integer>).

因此宣布这些方法是合法的.此外,方法调用表达式是有效的,因为通常只有一个方法匹配参数类型,因此通常是最具体的方法.

编辑:Yishai正确地指出在这种情况下还有另一个限制.在Java语言规范,部分8.4.8.3写道:

如果类型声明T具有成员方法m1并且存在以T形式声明的方法m2或T的超类型以使得满足以下所有条件,则为编译时错误:

  • m1和m2具有相同的名称.
  • m2可从T访问.
  • m1的签名不是m2签名的子签名(第8.4.2节).
  • m1或某些方法m1覆盖(直接或间接)具有与m2相同的擦除或某种方法m2覆盖(直接或间接).

附录:关于实现,以及缺乏

与流行的概念相反,方法签名中的泛型不会被删除.泛型以字节码(Java虚拟机的指令集)擦除.方法签名不是指令集的一部分; 它们被写入源代码中指定的类文件中.(另外,也可以在运行时使用反射查询此信息).

想一想:如果类型参数完全从类文件中删除,那么如果您没有JDK源,那么您所选择的IDE中的代码完成如何显示ArrayList.add(E)带有类型的参数E,而不是Object(=擦除E)附加代码?当方法参数的静态类型不是子类型时,编译器如何知道抛出编译错误E

  • 我认为案件比这更接近.考虑:如果将两个方法的返回类型更改为Object,则代码将不再编译.JLS没有说返回类型可以是方法签名的区别特征,以确定覆盖等效性.我最终同意Sun编译器是正确的,但这是一个非常接近的电话. (3认同)

dty*_*dty 2

如果 javac 有 bug,那也是有可能的。Javac 只是软件,与任何其他软件一样容易出现错误。

另外,Java 语言规范非常复杂,并且在某些地方有点模糊,因此这也可能是由于 Eclipse 人员和 javac 人员之间的解释差异所致。

我首先会在 Eclipse 支持渠道上询问这个问题。他们通常非常善于发现这些事情并解释为什么他们认为自己是对的,或者承认自己是错的。

根据记录,我认为 Eclipse 也在这里。