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

发布于 2024-12-22 11:44:02 字数 1505 浏览 0 评论 0原文

以下类:

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

在 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

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

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

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

问题 461:使用普通 ubuntu openjdk 进行 jclouds-core 编译失败

The following class:

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

compiles under Sun JDK 1.6.0_20 (Windows 64-bit, but shouldn't make a difference) but fails under Oracle JDK 1.7.0_01 (same platform) and OpenJDK 1.6.0_20 (Ubuntu) [1] with:

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

Why? How does the generic parameter (which should be erased, no?) cause this apparent mismatch. Note that removing generics as follows:

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

compiles and runs too (so the generics hack doesn't cause any weird runtime casting errors).

Issue 461: jclouds-core compilation fails using stock ubuntu openjdk

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

沉睡月亮 2024-12-29 11:44:02

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

不幸的是,根据规范的文字,javac7 是正确的。

这是由于java中的万恶之源——类型擦除。动机是在不破坏任何引用旧的、非泛化的集合 API 的旧代码的情况下泛化集合 API。为了简洁起见,我们将其称为“最愚蠢的动机”。

编译 BPrime.make() 时,首先 javac 需要找出包含该方法的类。这很容易就是B' 类。 ( http://java.sun.com/ docs/books/jls/third_edition/html/expressions.html#15.12.1

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

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

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

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

<块引用>

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

这意味着 ma 适用于表达式 BPrime.make(),因为 ma 不是 a通用方法。什么?!

规范解释了

上面的子句暗示非泛型方法可能适用于提供显式类型参数的调用。事实上,它可能会被证明是适用的。在这种情况下,类型参数将被忽略。

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

所以这也是为了服务于愚蠢的动机,我们必须允许像

    System.<String,Integer>currentTimeMillis();

Then这样的无意义语法,mbma都适用,因此歧义。

Obviously, the javac6's behavior is reasonable, and javac7's not.

Unfortunately, according to the letter of the spec, javac7 is right.

This is due to the root of all evil in java - type erasure. The motivation is to generify collection APIs without breaking any old code that reference the old, non-generified collection API. For the purpose of brevity let's refer to it as the dumbest motivation.

When compiling BPrime.<Object>make(), first javac needs to figure out the class containing the method. That is easily class B'. ( http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.1 )

Then we need to know all methods in class B', including inherited ones. That comes down to whether method make() (mb) in B' hides method make() (ma) in A; which comes down to whether signature of mb is a subsignature of ma. ( http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8 )

The existence of subsignature concept is also to serve the dumbest motivation. Otherwise we only need to worry about same signatures in determining overriding and hiding methods.

But that's not a problem this time. Per definition, mb is not a subsignature of ma, so ma is inherited in class B'. So class B' has two make() methods.

Next step, is to identify potentially applicable methods. The rule says ( http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.1 )

If the method invocation includes explicit type parameters, and the member is a generic method, then the number of actual type parameters is equal to the number of formal type parameters.

That means ma is applicable to expression BPrime.<Object>make(), because ma is not a generic method. What?!

The spec explains

The clause above implies that a non-generic method may be potentially applicable to an invocation that supplies explicit type parameters. Indeed, it may turn out to be applicable. In such a case, the type parameters will simply be ignored.

This rule stems from issues of compatibility and principles of substitutability. Since interfaces or superclasses may be generified independently of their subtypes, we may override a generic method with a non-generic one. However, the overriding (non-generic) method must be applicable to calls to the generic method, including calls that explicitly pass type parameters. Otherwise the subtype would not be substitutable for its generified supertype.

So this is also to serve the dumbest motivation, and we have to allow nonsense syntax like

    System.<String,Integer>currentTimeMillis();

Then, both mb and ma are applicable, thus the ambiguity.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文