在 Java 中用泛型方法重写泛型方法

2022-09-04 23:33:30

Angelica Langer在她的FAQ中说了关于泛型(参见Technialities.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
}

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

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

“Sub”中的“get()”与“Super”中的“get()”冲突;这两种方法具有相同的擦除,但都不能覆盖另一种方法。

但是当我运行程序时,它可以编译并执行而不会出现问题。我想IntelliJ在分析代码时存在某种错误,而正确的是Angelica在她的FAQ中讲述的内容。但我无法抓住重点。


答案 1

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

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


答案 2

我认为 的擦除与 的擦除不同或协变。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;
        }

推荐