简体   繁体   English

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

[英]Java generics code compiles with javac, fails with Eclipse Helios

I have the following test class that uses generics to overload a method. 我有以下测试类,它使用泛型来重载方法。 It works when compiled with javac and fails to compile in Eclipse Helios. 它在使用javac编译时有效,无法在Eclipse Helios中编译。 My java version is 1.6.0_21. 我的java版本是1.6.0_21。

All the articles I read indicate that Eclipse is right and this code should not work. 我读到的所有文章都表明Eclipse是对的,这段代码不适用。 However when compiled with javac and run, the right method is selected. 但是,使用javac和run编译时,会选择正确的方法。

How is this possible? 这怎么可能?

Thanks! 谢谢!

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);
    }
}

The Java Language Specification, section 8.4.2 writes: Java语言规范,第8.4.2节写道:

It is a compile-time error to declare two methods with override-equivalent signatures (defined below) in a class. 在类中声明具有覆盖等效签名(在下面定义)的两个方法是编译时错误。

Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1. 如果m1是m2的子签名或m2是m1的子签名,则两个方法签名m1和m2是覆盖等价的。

The signature of a method m1 is a subsignature of the signature of a method m2 if either 方法m1的签名是方法m2的签名的子签名(如果有的话)

  • m2 has the same signature as m1, or m2与m1具有相同的签名,或

  • the signature of m1 is the same as the erasure of the signature of m2. m1的签名与m2的签名擦除相同。

Clearly, the methods are not override equivalent, since ArrayList<String> isn't ArrayList (the erasure of ArrayList<Integer> ). 显然,这些方法不是覆盖等价的,因为ArrayList<String>不是ArrayListArrayList<Integer>的擦除)。

So declaring the methods is legal. 因此宣布这些方法是合法的。 Also, the method invocation expression is valid, as there trivially is a most specific method , since there is only one method matching the argument types. 此外,方法调用表达式是有效的,因为通常只有一个方法匹配参数类型,因此通常是最具体的方法

Edit : Yishai correctly points out that there is another restriction closely skirted in this case. 编辑 :Yishai正确指出在这种情况下还有另一个限制。 The Java Language Specification, section 8.4.8.3 writes: Java语言规范,第8.4.8.3节写道:

It is a compile time error if a type declaration T has a member method m1 and there exists a method m2 declared in T or a supertype of T such that all of the following conditions hold: 如果类型声明T具有成员方法m1并且存在以T形式声明的方法m2或T的超类型以使得满足以下所有条件,则为编译时错误:

  • m1 and m2 have the same name. m1和m2具有相同的名称。
  • m2 is accessible from T. m2可从T访问。
  • The signature of m1 is not a subsignature (§8.4.2) of the signature of m2. m1的签名不是m2签名的子签名(§8.4.2)。
  • m1 or some method m1 overrides (directly or indirectly) has the same erasure as m2 or some method m2 overrides (directly or indirectly). m1或某些方法m1覆盖(直接或间接)具有与m2相同的擦除或某种方法m2覆盖(直接或间接)。

Appendix: On ersure, and the lack thereof 附录:关于实现,以及缺乏

Contrary to popular notion, generics in method signatures are not erased. 与流行的概念相反,方法签名中的泛型不会被删除。 Generics are erased in bytecode (the instruction set of the Java virtual machine). 泛型以字节码 (Java虚拟机的指令集)擦除。 Method signatures are not part of the instruction set; 方法签名不是指令集的一部分; they are written to the class file as specified in the source code. 它们被写入源代码中指定的类文件中。 (As an aside, this information can also be queried at runtime using reflection). (另外,也可以在运行时使用反射查询此信息)。

Think about it: If type parameters were erased from class files entirely, how could the code completion in the IDE of your choice display that ArrayList.add(E) takes a parameter of type E , and not Object (=the erasure of E ) if you didn't have the JDKs source code attached? 想一想:如果类型参数完全从类文件中删除,那么您选择的IDE中的代码完成如何显示ArrayList.add(E)采用E类型的参数,而不是Object (= E的擦除)如果您没有附加JDK源代码? And how would the compiler know to throw a compilation error when the static type of the method argument wasn't a subtype of E ? 当方法参数的静态类型不是E的子类型时,编译器如何知道抛出编译错误?

This code is correct, as described in JLS 15.12.2.5 Choosing the Most Specific Method . 此代码是正确的,如JLS 15.12.2.5中选择最具体的方法所述

Also, consider coding to the interface: 另外,考虑编码到接口:

List<String> ss = new ArrayList<String>();
List<Integer> is = new ArrayList<Integer>();
// etc.

As @McDowell notes, the modified method signatures appear in the class file: 正如@McDowell所说,修改后的方法签名出现在类文件中:

$ javap build/classes/Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
    public Test();
    public static void main(java.lang.String[]);
    public java.lang.String getFirst(java.util.ArrayList);
    public java.lang.Integer getFirst(java.util.ArrayList);
}

Note that this does not contradict @meriton's observation about the class file. 请注意,这与@meriton关于类文件的观察结果并不矛盾。 For example, the output of this fragment 例如,此片段的输出

Method[] methods = Test.class.getDeclaredMethods();
for (Method m : methods) {
    System.out.println(Arrays.toString(m.getGenericParameterTypes()));
}

shows the formal parameter of main() , as well as the two generic type parameters: 显示main()的形式参数,以及两个泛型类型参数:

[class [Ljava.lang.String;]
[java.util.ArrayList<java.lang.String>]
[java.util.ArrayList<java.lang.Integer>]

Works for me in Eclipse Helios. 在Eclipse Helios中适合我。 Method selection occurs at compile time, and the compiler has enough information to do so. 方法选择在编译时进行,编译器有足够的信息来执行此操作。

It would be possible if javac had a bug in it. 如果javac有错误就有可能。 Javac is just software and is as liable to bugs as any other piece of software. Javac只是软件,并且像任何其他软件一样容易出错。

Plus, the Java Language Spec is very complex and a little bit vague in places so it could also be down to a difference in interpretation between the Eclipse guys and the javac guys. 另外,Java语言规范非常复杂并且在某些地方有点模糊,所以它也可能在Eclipse人员和javac人之间的解释上有所不同。

I would start by asking this on the Eclipse support channels. 我首先在Eclipse支持渠道上询问这个问题。 They're normally very good at picking up these things and explaining why they think they're right or else admitting that they're wrong. 他们通常非常善于接受这些事情并解释为什么他们认为他们是正确的或者承认他们错了。

For the record, I think Eclipse is right here, too. 为了记录,我认为Eclipse也就在这里。

你确定Eclipse也设置为使用Java 1.6吗?

After some research, I have the answer: 经过一番研究,我得到了答案:

The code, as it is specified above should NOT compile. 上面指定的代码不应该编译。 ArrayList<String> and ArrayList<Integer> , at runtime is still ArrayList . ArrayList<String>ArrayList<Integer> ,在运行时仍然是ArrayList But your code is not working because the returning type. 但是你的代码不起作用,因为返回类型。 If you set the same returning types for both methods, javac won't compile that... 如果为两个方法设置相同的返回类型,javac将不会编译...

I read that there is a bug in Java 1.6 (that is already fixed in Java 1.7) about this error. 我读到Java 1.6中存在一个关于此错误的错误(已在Java 1.7中修复)。 Is all about returning types... so, you will need to change the signature of your methods. 所有关于返回类型......所以,您需要更改方法的签名。

There is the bug 6182950 in Oracle's Bug Database . Oracle的Bug数据库中存在错误6182950

Eclipse and javac use different compilers. Eclipse和javac使用不同的编译器。 Eclipse uses a third party compiler to turn your code into bytecodes for the Java VM. Eclipse使用第三方编译器将您的代码转换为Java VM的字节码。 Javac uses the Java compiler than Sun publishes. Javac使用Java编译器而不是Sun发布。 Therefore it is possible that identical code produces slightly different results. 因此,相同的代码可能会产生略微不同的结果。 Netbeans I believe uses Sun's compiler so check it in there as well. Netbeans我相信使用Sun的编译器,所以也在那里检查它。

A point to keep in mind is that (all?, certainly some as for instance the NetBeans editor does this) static code analysis tools do not consider return type or modifiers (private/public etc.) as part of the method signature. 要记住的一点是(所有?,当然有些例如NetBeans编辑器这样做)静态代码分析工具不会将返回类型或修饰符(私有/公共等)视为方法签名的一部分。

If that is the case, then with the aid of type erasure both getFirst methods would get the signature getFirst(java.util.ArrayList) and hence trigger a name clash... 如果是这种情况,那么借助于类型擦除,两个getFirst方法都会获得签名getFirst(java.util.ArrayList) ,从而触发名称冲突......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM