首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么隐藏的静态方法在SunJDK6下编译,但在OpenJDK 6和7下导致编译失败?

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

Stack Overflow用户
提问于 2011-12-24 05:03:33
回答 1查看 1.2K关注 0票数 17

下面的类:

代码语言:javascript
复制
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 1.6.0_20 (Windows64位,但不会有什么影响)下编译,但在Oracle JDK 1.7.0_01 (相同平台)和OpenJDK 1.6.0_20 (Ubuntu) 1下失败,出现以下错误:

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

为什么?泛型参数(应该被擦除,否?)造成这种明显的不匹配。请注意,如下所示删除泛型:

代码语言:javascript
复制
...
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));
}

编译和运行(所以泛型hack不会导致任何奇怪的运行时类型转换错误)。

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-12-24 07:04:20

显然,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'中的所有方法,包括继承的方法。这归结于B'中的方法make() ( mb )是否隐藏了A中的方法make() ( ma );这归结为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.<Object>make(),因为ma不是泛型方法。什么?!

规格说明

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

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

所以这也是为了提供最愚蠢的动机,我们必须允许像这样的无意义语法

代码语言:javascript
复制
    System.<String,Integer>currentTimeMillis();

然后,mb和ma都是可应用的,从而产生歧义。

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8620606

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档