如何解决为什么不允许给超类final方法和子类方法同名?
在 Java 中,无法在与超类中的 final
方法同名的子类中编写方法背后的实际原因是什么? (请注意,我不是要覆盖该方法,这就是我放置关键字 final
的原因。)
请看下面的例子:
class A {
public final void method() {
System.out.println("in method A");
}
}
class B extends A {
public void method() {
System.out.println("in method B");
}
}
问题在IDE中表述为“'method()'不能覆盖'A'中的'method()';被覆盖的方法是final的”;但是,我想了解导致编译器失败的情况是什么。
解决方法
因为在 Java 中,覆盖不是可选的。
类级别的方法名称。
在类级别(例如,类文件中的内容以及 JVM 执行的内容),方法名称包括它们的返回类型和参数类型(当然还有名称)。在 JVM 级别,varargs 不存在(它是一个数组),泛型不存在(它们被删除用于签名),并且 throws
子句不是故事的一部分。但除此之外,这个方法:
public void foo(String foo,int bar,boolean[] baz,long... args) throws Exception {}
在类文件级别变成这个名字:
foo(Ljava/lang/String;I[Z[J)V
看起来像 gobbledygook,但 [
是“数组”,原语每个都有一个字母(Z 代表布尔值,J 代表长整数,I 代表整数),V 代表空,L 代表:对象类型。现在说得通了。
这确实是类级别的方法名称,有效(好吧,我们称其为签名)。 任何在java中调用方法,在类级别,总是使用完整的签名。这意味着 javac
根本无法编译方法调用,除非它确实知道您正在调用的确切方法,这就是为什么 javac
不起作用的原因,除非您拥有正在调用的所有内容的完整类路径可用编译时。
覆盖不是可选的!
在类级别,如果您定义的方法的完整签名与父类中的签名完全匹配,则它会覆盖该方法。时期。你不能不。 @Override
作为注释丝毫不会影响这一点(如果您没有覆盖任何内容,该注释只会导致编译器抱怨,这是编译器检查的文档,仅此而已)。
javac 更进一步
作为语言的东西,如果你想收紧返回类型,javac
会起到桥梁作用。鉴于:
class Parent {
Object foo() { return null; }
}
class Child extends Parent {
String foo() { return null; }
}
然后在类级别,Parent 中的一个方法的完整签名是 foo()Ljava/lang/Object;
而 Child 中的一个有 foo()Ljava/lang/String;
,因此这些不是相同的方法,Child 的 foo 将不会出现覆盖 Parent 的 foo。
但是 javac
会进行干预,并且确实会覆盖这些内容。它通过在 Child 中实际制作 2 个方法来做到这一点。你可以看到这个在行动!编写上述内容,编译它,然后在 Child 上运行 javap -c -v
,您会看到这些。 javac 有两种方法:foo()Ljava/lang/String;
和 foo()Ljava/lang/Object;
(它们具有相同的签名,因此根据定义覆盖了父级的实现)。第二个实现为仅调用“真实”foo(返回字符串的那个),并获取 synthetic
标志。
最终是什么
最终解决了您的问题:鉴于 final
说:我不能被覆盖,就是这样。您现在制定了 2 条互斥规则:
- 父的 foo 不能被覆盖
- 根据定义(因为它的签名匹配),Child 的 foo 会覆盖 Parent 的 foo
Javac 会就此结束,向您抛出错误,然后收工。如果你想象一些假设的 javac 更新,这些因素的组合应该导致 javac 制作一个单独的方法:但是,如何?在类级别,相同的签名 == 相同的方法(这是一个覆盖),那么您有什么建议?那个java在名字的末尾加了一个0
?
如果这是计划,javac 应该如何处理:
Parent p = new Child();
p.foo();
哪个 foo
用于那里? foo()Ljava/lang/Object;
来自家长,还是 foo0()L/java/Object;
来自孩子?
你可以写一个规范来回答这个问题(大概,这里很明显:Parent's foo;你写了 Child c = new Child(); c.foo();
然后 foo0
是有意的,但这使得语言变得相当复杂,目的是什么?
java 语言设计者认为这不是一个有用的练习,因此没有将这种复杂性添加到语言中。我很确定这显然是正确的决定,但您的意见当然可能会有所不同。
,Final 不仅意味着您无法覆盖它,还意味着您无法解决调用该方法的问题。
当您对对象进行子类化时,如果您可以创建一个隐藏最终方法的方法,那么您可以阻止超类方法运行,或者替换一些其他功能,而不是该对象的用户所期望的。这将允许引入恶意代码,并且会破坏使方法最终化的目的。
在您的情况下,听起来使超类方法最终可能不是最佳选择。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。