当前位置: 首页 > 知识库问答 >
问题:

为什么不允许给超类final方法和子类方法赋予相同的名称?[重复]

拓拔泉
2023-03-14

在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()'不能重写'method()';重写的方法是最终的;但是,我想了解这种情况导致编译器失败的原因。

共有2个答案

厉钊
2023-03-14

Final不仅意味着你不能重写它,还意味着你不能让这个方法被调用。

当对对象进行子类化时,如果可以创建一个方法来隐藏最终的方法,那么就可以阻止超类方法运行,或者替代对象用户所期望的其他功能。这将减少恶意代码的引入,并破坏最终方法的目的。

在你的例子中,听起来让超类方法最终化可能不是最好的选择。

鞠鸿雪
2023-03-14

因为在java中,重写不是可选的。

在类级别(例如,类文件中的内容和JVM执行的内容),方法名包括它们的返回类型和参数类型(当然还有名称)。在JVM级别,varargs不存在(相反,它是一个数组),泛型不存在(为了签名的目的,泛型被擦除),并且抛出子句不是故事的一部分。但除此之外,这种方法:

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代表void,L代表:对象类型。现在它有意义了。

实际上,这就是类级别的方法名(我们称之为签名)。在java中,在类级别对方法的任何调用都始终使用完整的签名。这意味着javac无法编译方法调用,除非它确实知道您正在调用的确切方法,这就是为什么javac无法工作,除非您在编译时拥有所有调用的完整类路径。

在类级别,如果定义了一个方法,其完整签名与父类中的签名完全匹配,那么它将覆盖该方法。时期你不能@Override注释对这一点没有丝毫影响(如果您没有覆盖任何内容,注释只会导致编译器抱怨,这是编译器检查的文档,仅此而已)。

作为一种语言,javac将为您提供更严格的返回类型。鉴于:

class Parent {
    Object foo() { return null; }
}

class Child extends Parent {
    String foo() { return null; }
}

然后在类级别,父级中的一个方法的完整签名是foo()Ljava/lang/Object foo()Ljava/lang/String

但是javac介入,并确实使这些覆盖。它通过在Child中实际制作两种方法来实现这一点。你可以在行动中看到这一点!编写上面的代码,编译它,然后在Child上运行javap-c-v,你就会看到这些。javac有两种方法:都是foo()Ljava/lang/Stringfoo()Ljava/lang/Object (它确实有相同的签名,因此根据定义,会覆盖父级的实现)。第二个函数被实现为只调用“real”foo(返回字符串的函数),并获得合成标志。

这最终解决了你的问题:既然final说:我不能被推翻,那么,就这样。您现在已经制定了两条相互排斥的规则:

  • 父母的意见不能被推翻
  • 孩子的foo,根据定义(因为它的签名匹配),凌驾于父母的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;have You writeChild c=new Child();c、 foo()然后foo0是有意的,但这使得语言变得非常复杂,目的是什么?

java语言设计者认为这不是一个有用的练习,因此没有给语言增加这种复杂性。我很确定这显然是正确的选择,但你的观点当然可能会有所不同。

 类似资料:
  • 为什么我的子类方法不重写其父类中相同签名的方法? 我有一个类,它扩展了一个抽象类,后者扩展了抽象类Actor(这里的上下文是一个pong游戏)。和都有一个具有相同签名的方法: 目的是让方法重写的方法。这里的问题是,当我在内部调用时,只调用的方法。 在中的上设置的断点会脱扣,而在中的上设置的断点从不脱扣。我还在方法上尝试了,但它仍然调用方法。 目标是让对象以与其他对象不同的速度上下移动。因此方法也会

  • 我对Java/Android中的继承/接口有点困惑,不确定我是否走上了正确的道路。基本上,我在Android系统中使用Parcelable,由于方法未定义,所以会出现错误。 我有一个动物超类和几个子类(狗、猫等)。在第一个活动中,您选择一只动物,然后它将其打包并传递给第二个活动: 问题是“updateImage”只存在于子类中,因此它不会在这里编译。我不希望这个方法出现在超类中,因为输出根据动物的

  • 如果我在java中有一个抽象的(或据我所知的)超类,如下所示: 我注意到,在重写这个方法时,子类可以使用自身作为返回类型,但不能使用参数: 为什么不允许这样做?我不是在寻求解决方案——我知道我可以在方法中使用< code>instanceof检查或其他方法,我更想知道为什么前者是可以接受的,而后者对编译器来说却不是。

  • 问题内容: 以下代码可以正常工作。在两个不同的结构上操作并打印该结构的字段的两种方法: 在控制台中显示所需的输出: 现在 ,如果我以以下方式更改方法签名,则会出现编译错误。我只是将方法的接收者移动到方法的参数: 我什至无法编译程序: 问 :为什么 当 方法具有相同的名称和Arity 时 ,我可以在接收器中互换结构类型,而不能在参数中互换结构类型? 问题答案: 因为Go不支持在其参数类型上重载用户定

  • 问题内容: 我有一个班和一个子班 好的,在05行中,id喜欢访问A类的方法测试。但是我陷入了循环,因为我不知道如何指定使用A类的方法。 有任何想法吗? 问题答案: 编辑:正如@Thilo提到的:避免在外部类和内部类中使用相同的方法名称,这将避免命名冲突。

  • 问题内容: 在PHP 5.2中启用严格警告之后,我看到了一个项目中的大量严格标准警告,这些项目最初编写时没有严格警告: 严格标准 : 静态函数 Program :: getSelectSQL()在Program.class.inc中 不应抽象 有问题的函数属于抽象父类Program,并且被声明为抽象静态,因为它应该在其子类(例如TVProgram)中实现。 我确实在这里找到有关此更改的参考: 删除