运行时的以下代码显然会打印出“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();
}
}
答案 0 :(得分:5)
我不相信;如果你在子类中覆盖method1()
,那就没有了。如果您确实需要这种行为,则必须将A.method1()
声明为final
,而无法在B
中对其进行定义。
这对我来说没有意义 - 如果你认为你需要,你应该重新考虑你的设计!
答案 1 :(得分:1)
您也可以将A.method1设为私有。然后,即使你有B.method1,也会调用A.method1。但我不确定,你应该检查
答案 2 :(得分:1)
是的,你可以做到。 在包 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 a.A;
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
答案 3 :(得分:1)
要做到这一点,你必须使用method1非虚方法。要做到这一点,你要做到最终:
public final void method1() {....
或者你把它私有化;在Java中,pivate方法总是非虚拟的:
private void method1() {....
(请注意,在C ++中,私有方法可能是虚拟的或非虚拟的;以及其他使得在C ++中实现模板方法模式更清洁的方法。)
以下是发生的事情:当通过对象引用调用非静态方法时(这是在Java中调用非静态对象方法的唯一方法),调用的方法取决于引用的对象的实际类型到(指向),而不是参考的类型。
在对象方法中,对该对象的其他方法(或成员)的调用隐含地以“this”为前缀。所以你在method2中对method1的调用确实是:
public void method2() {
this.method1();
System.out.println("A2");
}
this
是一个对象引用。在类A中的method2中,引用的类型为A,就像声明了this
一样:
A this;
但该引用指向B
类型的对象;它可以做到这一点,因为B派生自,继承,子类,is-a,A
。
正如我上面提到的,“当通过对象引用调用非静态方法时,调用的方法取决于引用的对象的实际类型,而不是引用的类型。”当您实例化一个B类型的对象并调用method2()时,您传入的this
(this
实际上是任何非静态函数的隐藏参数)是this
指向B对象。当B.method2()调用super()时,同样的this
被传递给A.method2()。
所以当A.method2()调用method1()时,真正发生的是我们用this.method1()
调用this
,它引用B
,{{1}你在B
中即时发送并在on。上调用了method2()。
由于method1() 是虚拟的,并且因为我们在对类型为main()
的对象的引用上调用method1(),所以编译器会确保B
'对method1()的重新定义是一个名为。
答案 4 :(得分:0)
使用标准的“自然”和广为接受的Java机制,你不能。 Java的设计是为了动态调度所有方法,除非是final或static(或调用super),在你的情况下。由于该方法在B中被重写,因此这些都不是一个选项。在特定情况下没有“禁用”动态调度的机制,但好消息是很少需要。
你可以通过反思克服一些这些限制,但这就像用大锤打开一件珍贵的礼物。
答案 5 :(得分:0)
即使我正在寻找这个解决方案。似乎不可能。 不能更改基类。即假设已经编写了基类并且您从中派生了一个类,那么在派生类中您无法做任何事情,以便基类调用自己的方法而不是重写方法。 如果要创建派生类的对象,基类将调用派生类的重写方法,而不是调用自己的方法。那是OOPS ......