用Java中的泛型方法覆盖泛型方法

时间:2015-02-27 13:03:33

标签: java generics

Angelica Langer在她关于泛型的常见问题解答中说(见Technicalities.FAQ822):

  

如果方法的类型参数具有不同的边界,那么它们   不要覆盖,因为这些方法的签名不是   覆盖当量。请记住,类型参数边界是其中的一部分   通用方法的签名。

     

示例(通用子类型方法重载泛型超类型   方法;不推荐):

     
class Super {
   public <T> void set( T arg) { ... }
   public <T> T get() { ... }
}
class Sub extends Super {
   public <S extends Number > void set( S arg) { ... } // overloads
   public <S extends Number > S get() { ... }         // overloads
}

我不明白为什么get方法在类Sub中被重载了。据我所知,它应该是一个编译时错误,因为getSubSuper中都有相同的签名(返回类型不是它的一部分)。

令我更加困惑的是,我用来测试代码的IDE(IntelliJ IDEA 14.0.3)在get中突出显示Sub作为下一条消息的编译错误:

  '''中的'get()'与'超级'中的'get()'冲突;两种方法都有相同的擦除,但都不会覆盖另一种方法。

但是当我运行程序时,它编译并执行没有问题。我认为IntelliJ在分析代码时存在某种错误,Angelica在她的常见问题解答中说的是正确的。但我无法理解这一点。

3 个答案:

答案 0 :(得分:1)

根据JLS,方法签名不包括返回类型,只包含方法名称及其参数类型。这意味着在编译Super和Sub时,编译错误应该返回,因为Sub.get()具有与Super.get()相同的擦除,但是既不覆盖也不重载Super.get()。它无法覆盖,因为有界类型X extends Number不是X类型的子类型,并且它不能重载,因为返回类型不是方法签名的一部分。在这种情况下,Sub.set会重载Super.set。

至于为什么你可以编译和运行它。如果您正在运行Java 6,那么Java 6中有一个已知的bug可以编译Super和Sub。在Java 7中,这是固定的,不允许使用。

答案 1 :(得分:0)

我认为public <S extends Number> S get()的删除与public <T> T get()的删除不同或协变。

编译并运行:

class B {
    public <T> T get() {
        return null;
    }

}

class A extends B {
    @Override
    public <S> S get() {
        return null;
    }

这不是:

class B {
        public <T> T get() {
            return null;
        }

    }

    class A extends B {
        @Override
        public <S extends Number> S get() {
            return null;
        }

答案 2 :(得分:0)

据我所知,为什么它重载而不是覆盖是因为编译器没有将它视为覆盖等效。进一步了解Angelika Langer常见问题解答,你会看到它讨论为什么覆盖一个方法不符合预期。

http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ051

总而言之,这些方法具有相同的名称和参数,但基于通用子类型被定义为不同。即使使用覆盖注释,编译器也会将其视为重载方法。在使用泛型时,方法有一个附加层,其中泛型类型作为方法的一部分应用,可以唯一地定义它。