为什么方法覆盖在使用泛型时不检查方法签名?

why method overriding does not check method signature when using generics?

例如,我们知道以下是不允许的,因为覆盖需要相同的方法签名。

class Parent{
  void f(Parent p){}
}

class Son extends Parent{
 @Override//not allowed ,change the signature
 void f(Son s){} 
}

但是,如果我们使用泛型,比如

class Parent<T extends Parent>{
  void f(T p){}
}

class Son extends Parent<Son>{
 @Override//ok 
 void f(Son s){} 
}

那么它是允许的,但是为什么?

重载条件:

1.Both 必须具有相同的方法名称。

2.Both 必须有不同的参数列表。

3.Have 种不同的 return 类型。

4.Have 不同的访问修饰符。

5.Throw 个不同的已检查或未检查异常。

您的第二个代码不符合条件 2。

T是一种类型。表示任何类型为 Parent 的子类都是可接受的。即,Son(Subclass) 或 Parent(Parent)

所以,由于相同的参数列表。它被认为是覆盖。

f(Parent p)void f(Son s)是不同的方法,具有不同的签名。由于它们具有相同的名称,因此被称为重载

泛型是编译器与 JVM 玩弄的把戏。当您定义 Parent<T extends Parent>f(T p) 时,编译器将能够 假装 T 可以是 [=16 的任何 sub-type =],取决于引用 Parent 时为 T 指定的类型参数。

对于 JVM,方法仍然创建为 f(Parent p),因为这是 Tbound。这就是 类型擦除 的意义所在。

当您随后声明 Son extends Parent<Son> 时,编译器 假装 f(T p) 被声明为 f(Son p),这将使 f(Son s) class 的方法 Son 覆盖 Parent 中的 f 方法。然而,这只是编译器假装,JVM 只看到 super-type 中的方法 f(Parent p),因此 Son 中的 f 不是 [=26= 的重写] Parent.

中的方法

为了实现泛型添加到 Java 语言的假想世界观,编译器通过创建 桥接方法 来解决差异。结果是,在type-erasure之后,JVM看到的运行时代码将是:

class Parent {
    void f(Parent p) {}
}

class Son extends Parent {
    @Override
    bridge synthetic void f(Parent p) {
        this.f((Son) p);
    }

    void f(Son s) {}
}

bridgesynthetic 当然不是可以在 Java 源代码中使用的关键字,但它们是内部 修饰符 指定的字节码中的方法。

如你所见,如果某人有一个 Parent<Son> parent = new Son(); 变量并调用 parent.f(son),隐藏桥方法将 重定向f(Son s)方法。

都是编译器玩的把戏,让Java语言适合JVM执行的字节码。