为什么方法覆盖在使用泛型时不检查方法签名?
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)
,因为这是 T
的 bound。这就是 类型擦除 的意义所在。
当您随后声明 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) {}
}
bridge
和 synthetic
当然不是可以在 Java 源代码中使用的关键字,但它们是内部 修饰符 指定的字节码中的方法。
如你所见,如果某人有一个 Parent<Son> parent = new Son();
变量并调用 parent.f(son)
,隐藏桥方法将 重定向 对 f(Son s)
方法。
都是编译器玩的把戏,让Java语言适合JVM执行的字节码。
例如,我们知道以下是不允许的,因为覆盖需要相同的方法签名。
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)
,因为这是 T
的 bound。这就是 类型擦除 的意义所在。
当您随后声明 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) {}
}
bridge
和 synthetic
当然不是可以在 Java 源代码中使用的关键字,但它们是内部 修饰符 指定的字节码中的方法。
如你所见,如果某人有一个 Parent<Son> parent = new Son();
变量并调用 parent.f(son)
,隐藏桥方法将 重定向 对 f(Son s)
方法。
都是编译器玩的把戏,让Java语言适合JVM执行的字节码。