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

huangapple go评论66阅读模式
英文:

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

问题

以下是翻译好的部分:

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

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

class Son extends Parent{
 @Override//不允许,改变了方法签名
 void f(Son s){} 
}

然而,如果我们使用泛型,就像这样:

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

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

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

英文:

for example, we know that the following is not allowed because overriding need the same method signature.

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

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

However, if we use generics, like

class Parent&lt;T extends Parent&gt;{
  void f(T p){}
}

class Son extends Parent&lt;Son&gt;{
 @Override//ok 
 void f(Son s){} 
}

then it is allowed , but why?

答案1

得分: 2

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

泛型是编译器与 JVM 一起使用的一种技巧。当你定义 Parent&lt;T extends Parent&gt;f(T p) 时,编译器会假装 T 可以是 Parent 的任何子类型,这取决于在引用 Parent 时为 T 指定的类型参数。

对于 JVM 来说,该方法仍然被创建为 f(Parent p),因为这是 T边界。这就是类型擦除的含义所在。

然后,当你声明 Son extends Parent&lt;Son&gt; 时,编译器会假装 f(T p) 被声明为 f(Son p),这会使得类 Son 中的 f(Son s) 方法成为 Parentf 方法的一个覆盖。然而,这只是编译器的假装,JVM 只会在超类型中看到方法 f(Parent p),因此 Son 中的 f 不是 Parentf 方法的覆盖。

为了实现泛型为 Java 语言添加的这种假象视图,编译器通过创建一个桥接方法来解决这种差异。结果是,在类型擦除之后,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&lt;Son&gt; parent = new Son(); 变量,并调用 parent.f(son),隐藏的桥接方法将会重定向调用到 f(Son s) 方法。

这都是编译器在发挥技巧,将 Java 语言适应到 JVM 执行的字节码中。

英文:

f(Parent p) and void f(Son s) are different methods, having different signatures. Since they have the same name, if is referred to as overloading.

Generics is the compiler playing tricks with the JVM. When you define Parent&lt;T extends Parent&gt; and f(T p), the compiler will be able to pretend that T can be any sub-type of Parent, depending on the type argument specified for T when Parent is referenced.

To the JVM, the method is still created as f(Parent p), since that is the bound for T. This is what type erasure is about.

When you then declare Son extends Parent&lt;Son&gt;, the compiler pretends that f(T p) was declared as f(Son p), which would make f(Son s) method of class Son an override of the f method in Parent. However, that is just the compiler pretending, the JVM only sees method f(Parent p) in the super-type, so the f in Son is not an override of the f method in Parent.

To implement the pretended view of the world that generics has added to the Java language, the compiler solves the discrepancy by creating a bridge method. The result is that, after type-erasure, the runtime code seen by the JVM will be:

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) {}
}

bridge and synthetic are of course not keywords you can use in Java source code, but they are internal modifiers specified on the method in the bytecode.

As you can see, if someone has a Parent&lt;Son&gt; parent = new Son(); variable and calls parent.f(son), the hidden bridge method will redirect the call to the f(Son s) method.

It is all the compiler playing tricks to fit the Java language into the bytecode that the JVM executes.

答案2

得分: 1

方法重载的条件:

  1. 两者必须有相同的方法名称。

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

  3. 必须有不同的返回类型。

  4. 必须有不同的访问修饰符。

  5. 必须抛出不同的已检查或未检查异常。

你的第二段代码未满足条件2。

T 是一个类型。"" 的意思是任何类型为 Parent 其子类的东西都是可接受的。即,Son(子类)或 Parent(父类)。

所以,由于参数列表相同,被视为覆盖(Overriding)。

英文:

The conditions for Overloading :

1.Both must have the same method name.

2.Both must have different argument lists.

3.Have different return types.

4.Have different access modifiers.

5.Throw different checked or unchecked exceptions.

Your Second code Fails to meet condition number 2.

T is a type. <T extends Parent> means anything that is of type Parent or its subclass is acceptable. ie, Son(Subclass) or Parent(Parent)

So, Due to Same Argument lists. It is considered as Overriding.

huangapple
  • 本文由 发表于 2020年9月8日 18:48:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/63792336.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定