似乎无法理解复杂的多态性

2022-09-03 06:35:00

我正在学习CS,我们有关于多态性的问题,我无法理解。下面是一个示例:

public class AA{
    public AA(){
        foo();
    }
    private void foo() {
        System.out.print("AA::foo ");
        goo();
    }
    public void goo(){
        System.out.print("AA::goo ");
    }
}

public class BB extends AA{
    public BB(){
        foo();
    }
    public void foo(){
        System.out.print("BB:foo ");
    }
    public void goo(){
        System.out.print("BB::goo ");
    }
    public static void main(String[] args){
        // Code goes here
    }
}

当在 void main i 添加行:

AA a = new BB();

它首先使用AA构造函数打印AA:foo,然后goo()将其发送到BB的goo,为什么会这样?

简单的多态性,如“动物 - >猫/蜘蛛/狗”很容易理解,但当涉及到这一点时,我只是迷失了。你们能给我一些关于如何阅读这段代码的提示吗?规则是什么?

编辑:没有注释,因为这是考试中的问题。@Override


答案 1

解释

public class AA {

    private void foo() { ... }
    ^^^^^^^

}

多态性不适用于方法。子类不继承方法,因此无法重写它们:privateprivate

类从其直接超类继承超类的所有具体方法(静态方法和实例方法),其所有方法都为真:Cm

  • m是 的直接超类的成员。C
  • m公共的、受保护的,或者在与 C 相同的包中使用包访问权限进行声明。
  • 中声明的任何方法的签名都不是 的签名的签名。Cm

Java 语言规范 - 8.4.8.继承、覆盖和隐藏

因此,来自构造函数的调用不会调用 ,它会调用 。foo()ABB#fooAA#foo

但是其中的调用是指被覆盖的方法。在这里,通过方法,应用了方法覆盖和多态性。goo()AA#fooBB#goopublic


这有点棘手,所以我建议你把@Override注释放在它应该在任何地方。

public class BB extends AA {

    @Override   // it doesn't compile - no overriding here
    public void foo() { ... }
    @Override   // it does override
    public void goo() { ... }

}

检测另一个问题也可能有所帮助:

程序员偶尔会重方法声明,当他们打算重写它时,会导致微妙的问题。注释类型支持及早发现此类问题。Override

如果类型中的方法声明带有 注释,但该方法没有从 超类型 中声明的方法重写,或者不覆盖等效于 的公共方法,则会发生编译时错误。T@OverrideTTObject

Java 语言规范 - 9.6.4.4.@Override

插图

如果构造函数体不以显式构造函数调用开始,并且被声明的构造函数不是原始类 Object 的一部分,则构造函数体隐式地以超类构造函数调用开始,该调用其直接超类的构造函数,不带任何参数。super();

Java 语言规范 - 8.8.7.构造函数主体

简单地说,

public BB() {
    foo();
}

变成

public BB() {
    super();
    foo();
}

请记住,我们可以制作下一个插图:super();

new BB()
        AA()                       // super(); -> AA constructor
                A#foo()            // private method call 
                        B#goo()    // polymorphic method call
        BB()                       // BB constructor
                B#foo()            // plain method call 

答案 2

这在官方文档中得到了很好的解释:

https://docs.oracle.com/javase/tutorial/java/IandI/super.html

如果构造函数未显式调用超类构造函数,则 Java 编译器会自动插入对超类的无参数构造函数的调用。如果超类没有无参数构造函数,您将收到编译时错误。对象确实有这样的构造函数,所以如果对象是唯一的超类,那就没有问题了。

因此,Java编译器正在为你添加没有args的super()。

实际上,如果要扩展的类没有默认构造函数,则需要在之前使用 args 调用此构造函数。


否则,不被调用的原因是因为它被覆盖,即使它没有注释,如果你想看到那个调用,你需要使用super();在你的b:goo方法中。事实上,foo不是覆盖的,因为它是私有的,所以不可能覆盖它,如果你试图添加注释@Override你会得到编译失败。AA:gooBB@Override


推荐