为什么我可以从超类访问重写的子类方法?
Why can I access overridden subclass methods from the superclass?
为什么method()
调用覆盖的子类method2
而不是BaseClass
中的method2
?
public class BaseClass {
public void method(){
System.out.println("method() called");
method2();
}
public void method2(){
System.out.println("method2() called");
}
}
public class ChildClass extends BaseClass {
public void method2(){
System.out.println("method2() from BaseClass");
}
}
public class Main {
public static void main(String[] args) {
ChildClass obj = new ChildClass();
obj.method();
}
}
这就是运行时多态(Dynamic Method Dispatch)的概念。因为你正在将 ChildClass
的对象(实例)分配给 obj
引用变量,它会调用子 class.
的 method
总是首先调用创建实例的 class 的方法。如果该方法不存在于该特定子项 class,则将调用父项的继承方法。
如果你来自C++角:
- Java 中的所有实例方法(非静态)都是
virtual
。
- 所有 class 方法(静态)都不是。
这就是你的情况发生的原因。
这也是为什么 Java 编译器会抱怨(警告)如果你通过对象访问静态方法,你应该通过不同的 class 调用,因为调用“对象的静态方法”可能不明确,因为它可能是两个具有相同签名的静态方法被调用。
扩展您的示例:
package Whosebug.staticcalls;
public class BaseClass {
public void method() {
System.out.println("method() called");
method2();
}
public void method2() {
System.out.println("method2() called");
}
static public void sayHello() {
System.out.println("BaseClass.sayHello()");
}
}
和
package Whosebug.staticcalls;
public class ChildClass extends BaseClass {
public void method2() { // compiler warning: The method method2() of type ChildClass should be tagged with @Override since it actually overrides a superclass method
System.out.println("method2() from BaseClass");
}
public void originalCallToBaseMethod2() {
super.method2(); // will run BaseClass.method2()
}
static public void sayHello() {
System.out.println("ChildClass.sayHello()");
}
}
和
package Whosebug.staticcalls;
public class Main {
public static void main(final String[] args) {
final ChildClass obj = new ChildClass();
System.out.println("\nCalling obj.method(); ...");
obj.method();
System.out.println("\nCalling obj.sayHello(); ...");
obj.sayHello(); // compiler warning: The static method sayHello() from the type ChildClass should be accessed in a static way
System.out.println("\nCalling ChildClass.sayHello(); ...");
ChildClass.sayHello(); // the proper call
System.out.println("\nCalling BaseClass.sayHello(); ...");
BaseClass.sayHello(); // but you can also explpicitly call the other method
System.out.println("\nCalling obj.originalCallToBaseMethod2(); ...");
obj.originalCallToBaseMethod2(); //
}
}
这里你可以看到我说的例子。
注意:在Main.main()的最后一次调用中,我们仍然可以调用BaseClass.method2(),但不能直接调用。我们必须在 ChildClass 中才能做到这一点,这是通过 super
keyword/reference.
完成的
一点题外话,以完成寻址模式:
如果你在内部 class 中并且需要调用外部 class 中的遮蔽名称,你可以使用 Outer.this.method()
:
package Whosebug.staticcalls;
import Whosebug.staticcalls.OuterInner.Outer.Inner;
public class OuterInner {
class Outer {
void method() {
System.out.println("OuterInner.Outer.method()");
}
class Inner {
void method() {
System.out.println("OuterInner.Outer.Inner.method()");
}
void callOuter() {
Outer.this.method();
}
}
}
public static void main(final String[] args) {
final OuterInner oi = new OuterInner();
final Outer outer = oi.new Outer();
final Inner inner = outer.new Inner();
System.out.println("\nCalling inner.method(); ...");
inner.method();
System.out.println("\nCalling inner.callOuter(); ...");
inner.callOuter();
}
}
为什么method()
调用覆盖的子类method2
而不是BaseClass
中的method2
?
public class BaseClass {
public void method(){
System.out.println("method() called");
method2();
}
public void method2(){
System.out.println("method2() called");
}
}
public class ChildClass extends BaseClass {
public void method2(){
System.out.println("method2() from BaseClass");
}
}
public class Main {
public static void main(String[] args) {
ChildClass obj = new ChildClass();
obj.method();
}
}
这就是运行时多态(Dynamic Method Dispatch)的概念。因为你正在将 ChildClass
的对象(实例)分配给 obj
引用变量,它会调用子 class.
method
总是首先调用创建实例的 class 的方法。如果该方法不存在于该特定子项 class,则将调用父项的继承方法。
如果你来自C++角:
- Java 中的所有实例方法(非静态)都是
virtual
。 - 所有 class 方法(静态)都不是。
这就是你的情况发生的原因。
这也是为什么 Java 编译器会抱怨(警告)如果你通过对象访问静态方法,你应该通过不同的 class 调用,因为调用“对象的静态方法”可能不明确,因为它可能是两个具有相同签名的静态方法被调用。
扩展您的示例:
package Whosebug.staticcalls;
public class BaseClass {
public void method() {
System.out.println("method() called");
method2();
}
public void method2() {
System.out.println("method2() called");
}
static public void sayHello() {
System.out.println("BaseClass.sayHello()");
}
}
和
package Whosebug.staticcalls;
public class ChildClass extends BaseClass {
public void method2() { // compiler warning: The method method2() of type ChildClass should be tagged with @Override since it actually overrides a superclass method
System.out.println("method2() from BaseClass");
}
public void originalCallToBaseMethod2() {
super.method2(); // will run BaseClass.method2()
}
static public void sayHello() {
System.out.println("ChildClass.sayHello()");
}
}
和
package Whosebug.staticcalls;
public class Main {
public static void main(final String[] args) {
final ChildClass obj = new ChildClass();
System.out.println("\nCalling obj.method(); ...");
obj.method();
System.out.println("\nCalling obj.sayHello(); ...");
obj.sayHello(); // compiler warning: The static method sayHello() from the type ChildClass should be accessed in a static way
System.out.println("\nCalling ChildClass.sayHello(); ...");
ChildClass.sayHello(); // the proper call
System.out.println("\nCalling BaseClass.sayHello(); ...");
BaseClass.sayHello(); // but you can also explpicitly call the other method
System.out.println("\nCalling obj.originalCallToBaseMethod2(); ...");
obj.originalCallToBaseMethod2(); //
}
}
这里你可以看到我说的例子。
注意:在Main.main()的最后一次调用中,我们仍然可以调用BaseClass.method2(),但不能直接调用。我们必须在 ChildClass 中才能做到这一点,这是通过 super
keyword/reference.
一点题外话,以完成寻址模式:
如果你在内部 class 中并且需要调用外部 class 中的遮蔽名称,你可以使用 Outer.this.method()
:
package Whosebug.staticcalls;
import Whosebug.staticcalls.OuterInner.Outer.Inner;
public class OuterInner {
class Outer {
void method() {
System.out.println("OuterInner.Outer.method()");
}
class Inner {
void method() {
System.out.println("OuterInner.Outer.Inner.method()");
}
void callOuter() {
Outer.this.method();
}
}
}
public static void main(final String[] args) {
final OuterInner oi = new OuterInner();
final Outer outer = oi.new Outer();
final Inner inner = outer.new Inner();
System.out.println("\nCalling inner.method(); ...");
inner.method();
System.out.println("\nCalling inner.callOuter(); ...");
inner.callOuter();
}
}