为什么隐藏的静态方法在Sun JDK 6下编译但在OpenJDK 6和7下导致编译失败?

And*_*ips 17 java generics java-7

以下课程:

public class StaticMethodsDemo {

    public static class A {
        public static A make() { return new A(); };
    }
    public static class B extends A {
        public static B make() { return new B(); };
    }
    public static class BPrime<T> extends A {
        public static <T> BPrime<T> make() { return new BPrime<T>(); };
    }

    public static void main(String[] args) {
        B.make();
        // compiles under Sun JDK 1.6.0_20 but fails under Oracle JDK 1.7.0_01. Why?
        BPrime.<Object>make();
    }
}
Run Code Online (Sandbox Code Playgroud)

在Sun JDK 1.6.0_20下编译(Windows 64位,但不应该有所作为),但在Oracle JDK 1.7.0_01(同一平台)和OpenJDK 1.6.0_20(Ubuntu)[1]下失败:

[ERROR] StaticMethodsDemo.java:[37,14] error: reference to make is ambiguous, both method make() in A and method <T>make() in BPrime match
Run Code Online (Sandbox Code Playgroud)

为什么?通用参数(应该擦除,不是?)如何导致这种明显的不匹配.请注意删除泛型如下:

...
public static class BPrime<T> extends A {
    T val;
    public static BPrime<?> make() { return new BPrime<Object>(); };
    public void setT(T val) { this.val = val; }
}

public static void main(String[] args) {
    B.make();
    BPrime<Long> bprime = (BPrime<Long>) BPrime.make();
    bprime.setT(Long.valueOf(10));
}
Run Code Online (Sandbox Code Playgroud)

编译和运行(因此泛型黑客不会导致任何奇怪的运行时转换错误).

问题461:jclouds-core编译使用库存ubuntu openjdk失败

irr*_*ble 12

显然,javac6的行为是合理的,而javac7则不然.

不幸的是,根据规范的字母,javac7是对的.

这是由于java中所有邪恶的根源 - 类型擦除.其动机是在不破坏任何引用旧的非泛化集合API的旧代码的情况下生成集合API.为了简洁起见,我们将其称为最愚蠢的动机.

编译时BPrime.<Object>make(),第一个javac需要找出包含该方法的类.这很容易上课B'.(http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.1)

然后我们需要知道类中的所有方法B',包括继承的方法.这归结为是否 隐藏方法(ma)中的方法make()(mb); 归结为mb的签名是否是ma的子签名.(http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8)B'make()A

子签名概念的存在也是为了服务于最愚蠢的动机.否则,我们只需要在确定覆盖和隐藏方法时担心相同的签名.

但这次不是问题.根据定义,mb不是ma的子签名,因此ma在类中继承B'.所以班B'有两种make()方法.

下一步是确定可能适用的方法.规则说(http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.1)

如果方法调用包含显式类型参数,并且成员是泛型方法,则实际类型参数的数量等于正式类型参数的数量.

这意味着毫安适用于表达BPrime.<Object>make(),因为毫安不是一个通用的方法.什么?!

规范解释道

上面的条款暗示非泛型方法可能适用于提供显式类型参数的调用.实际上,它可能会变得适用.在这种情况下,将简单地忽略类型参数.

该规则源于兼容性和可替代性原则的问题.由于接口或超类可以独立于其子类型进行泛化,因此我们可以使用非泛型方法覆盖泛型方法.但是,重写(非泛型)方法必须适用于对泛型方法的调用,包括显式传递类型参数的调用.否则,子类型不能替代其生成的超类型.

所以这也是为了服务于最愚蠢的动机,我们必须允许无意义的语法

    System.<String,Integer>currentTimeMillis();
Run Code Online (Sandbox Code Playgroud)

然后,mbma都适用,因此模糊.