强制调用基类方法

运行时的以下代码显然会打印出“B1 / A2 / B2”。 现在,是否有可能打印“A1 / A2 / B2”(即A#method2()应该在A上调用method1(),而不是在B上调用?)

注意:我没有这样的需要获得传递多态性,这个问题仅仅是出于好奇。

class A { public void method1() { System.out.println("A1"); } public void method2() { method1(); System.out.println("A2"); } } class B extends A { @Override public void method2() { super.method2(); System.out.println("B2"); } @Override public void method1() { System.out.println("B1"); } } public class Tmp { public static void main(String args[]) { B b = new B(); b.method2(); } } 

是的,你可以做到。 在包a中定义A:

 package a; public class A { void method1() { System.out.println("A1"); } public void method2() { method1(); System.out.println("A2"); } } 

在包b中定义B:

 package b; import aA; public class B extends A { @Override public void method2() { super.method2(); System.out.println("B2"); } void method1() { System.out.println("B1"); } } 

将测试放入包a并运行它。 结果是A1 / A2 / B2。 当然这是不健康的:注意方法1上必要的@Override遗漏 – 如果你把它放回去,你将得到编译器错误:

 method does not override or implement a method from a supertype 

我不相信; 如果你在子类中重写method1() ,那就没有了。 如果你真的需要这种行为,你必须将A.method1()声明为final ,而你无法在B定义它。

这样做对我来说没有意义 – 如果你认为你需要,你应该重新考虑你的设计!

您也可以将A.method1设为私有。 然后,即使你有B.method1,也会调用A.method1。 但我不确定,你应该检查一下

要做到这一点,你必须方法method1非虚拟。 要做到这一点,你要做到最终:

  public final void method1() {.... 

或者你把它私有化; 在Java中,pivate方法总是非虚拟的:

  private void method1() {.... 

(请注意,在C ++中,私有方法可以是虚方法还是非虚方法;以及其他使得在C ++中实现模板方法模式更清洁的方法。)

这是正在发生的事情:当通过对象引用调用非静态方法时(这是在Java中调用非静态对象方法的唯一方法),调用的方法取决于引用的对象的实际类型(指向) -to),而不是引用的类型。

在对象方法中,对该对象的其他方法(或成员)的调用隐含地以“this”为前缀。 所以你在method2中对method1的调用确实是:

 public void method2() { this.method1(); System.out.println("A2"); } 

this是一个对象参考。 在类A中的method2中,引用的类型为A,就好像已声明:

 A this; 

但该引用指向B类对象; 它可以做到这一点,因为B派生自,inheritance,子类,是-a, A

正如我上面提到的,“当通过对象引用调用非静态方法时,调用的方法取决于引用的对象的实际类型,而不是引用的类型。” 当你实例化一个B类型的对象,并调用method2()时,你传入的thisthis实际上是任何非静态函数的隐藏参数)都是指向B对象的。 当B.method2()调用super()时,将其传递给A.method2()。

所以当A.method2()调用method1()时,真正发生的事情就是我们用相同的方法调用this.method1() ,它指的是一个B ,你在main()实现的B ,并且调用了method2()on。

由于method1() 虚拟的,并且因为我们在对类型B的对象的引用上调用method1(),所以编译器确保B对method1()的重新定义是被调用的。

使用标准的“自然”和广为接受的Java机制,你不能。 Java的设计是为了动态调度所有方法,除非是final或static(或调用super),在你的情况下。 由于该方法在B中被重写,因此这些都不是一个选项。 在特定情况下没有“禁用”动态调度的机制,但好消息是很少需要。

你可以通过反思来克服一些这些限制,但这就像用大锤打开一件珍贵的礼物。

即使我正在寻找这个解决方案。 似乎不可能。 不能更改基类。 即假设已经编写了基类并且您从中派生了一个类,那么在派生类中您无法做任何事情,以便基类调用自己的方法而不是重写方法。 如果要创建派生类的对象,基类将调用派生类的重写方法,而不是调用自己的方法。 这是OOPS ….