Java继承的行为不符合预期
需要以下上下文:这种编码方式的目的是避免if-else语句和instanceof;这总是个坏主意 我有3门课,签名如下:Java继承的行为不符合预期,java,inheritance,Java,Inheritance,需要以下上下文:这种编码方式的目的是避免if-else语句和instanceof;这总是个坏主意 我有3门课,签名如下: abstract class A {} class B extends A {} class C extends A {} 然后我有另一个具有以下结构的类: class MyClass { private final A model; public MyClass(A m) { this.model = m; } publ
abstract class A {}
class B extends A {}
class C extends A {}
然后我有另一个具有以下结构的类:
class MyClass {
private final A model;
public MyClass(A m) {
this.model = m;
}
public void doSomething() {
System.out.println(this.model instanceof C); //TRUE!!
execute(this.model);
}
private void execute(A m) {
System.out.println("noo");
}
private void execute(C m) {
System.out.println("yay");
}
}
最后是我的主要内容:
public static void main(String... args) {
C mod = new C();
MyClass myClass = new MyClass(mod);
myClass.doSomething();
}
现在问题来了,;executeC方法永远不会被执行,它总是executeA方法。我怎样才能解决这个问题?我无法将executeA方法的签名更改为executeB,因为这会导致一个错误,即java无法在MyClassdoSomething解析方法executeA。方法重载在编译时解析。在编译时,m的类型是A,因此executeA m被执行 此外,私有方法是不可重写的
解决方案是使用@OliverCharlesworth建议的访问者模式。方法重载在编译时解决。在编译时,m的类型是A,因此executeA m被执行 此外,私有方法是不可重写的
解决方案是使用@OliverCharlesworth建议的访问者模式。您的代码说明了对象的静态类型和动态类型之间的区别。静态类型是编译器已知的类型;动态类型是运行时实际存在的类型 模型字段的静态类型为: 也就是说,编译器知道一个模型本身或它的一些实现将被分配给该模型。编译器不知道其他任何东西,因此在选择executeA m和executeC m时,它唯一的选择是executeA m。该方法根据对象的静态类型进行解析 另一方面,instanceof理解动态类型。它可以判断模型被设置为C,因此在打印输出中报告真值 您可以通过向a添加一个方法并在B和C中重写它来解决此问题,以路由到正确的执行: 请注意,这两种实现都调用
back.execute(this);
但是,B中的实现的类型是B,C中的实现的类型是C,因此调用被路由到MyClass的execute方法的不同重载
我无法将executeA方法的签名更改为executeB
还要注意,现在您也可以而且应该这样做,因为回调是根据对象的类型执行到正确的重载的。您的代码说明了对象的静态类型和动态类型之间的区别。静态类型是编译器已知的类型;动态类型是运行时实际存在的类型 模型字段的静态类型为: 也就是说,编译器知道一个模型本身或它的一些实现将被分配给该模型。编译器不知道其他任何东西,因此在选择executeA m和executeC m时,它唯一的选择是executeA m。该方法根据对象的静态类型进行解析 另一方面,instanceof理解动态类型。它可以判断模型被设置为C,因此在打印输出中报告真值 您可以通过向a添加一个方法并在B和C中重写它来解决此问题,以路由到正确的执行: 请注意,这两种实现都调用
back.execute(this);
但是,B中的实现的类型是B,C中的实现的类型是C,因此调用被路由到MyClass的execute方法的不同重载
我无法将executeA方法的签名更改为executeB
还请注意,现在您也可以而且应该这样做,因为回调是根据这种类型执行到正确重载的。方法重载是编译时多态性。因此,为了调用方法executeC,您需要将模型定义为类C。 最好在类A中定义方法execute并在子类中重写它
abstract class A {
abstract void execute();
}
class B extends A {
public void execute(){};
}
class C extends A {
public void execute(){};
}
然后:
class MyClass {
private final A model;
public void doSomething() {
model.execute();
}
使用多态性来避免if-else语句和instanceof-checking方法重载的更好方法是编译时多态性。因此,为了调用方法executeC,您需要将模型定义为类C。 最好在类A中定义方法execute并在子类中重写它
abstract class A {
abstract void execute();
}
class B extends A {
public void execute(){};
}
class C extends A {
public void execute(){};
}
然后:
class MyClass {
private final A model;
public void doSomething() {
model.execute();
}
使用多态性可以更好地避免if-else语句和instanceof-checking您在构造函数中将类型C的对象作为类型A的对象发送,您已经完成了向上转换,并将其分配给类型A的引用,这将导致只调用executeA方法。您可以检查该对象是否是C的实例,具体取决于对于结果,调用所需的方法。你可以这样做
public void doSomething(){
System.out.println(model instanceof C);
if (model instanceof C) execute((C)model);
else
execute(model);
}
您正在将C类型的对象作为构造函数中的A类型对象发送,您已经完成了向上转换,并将其分配给A类型的引用,这将导致只调用executeA方法。您可以检查该对象是否是C的实例,并根据结果调用所需的方法。你可以这样做
public void doSomething(){
System.out.println(model instanceof C);
if (model instanceof C) execute((C)model);
else
execute(model);
}
这个问题可以在某种程度上解决
ost通过.A model->executeA,有什么问题吗?@OliverCharlesworth还没有听说过这个问题;这看起来确实很有趣。这可以通过.A model->executeA来解决,有什么问题吗?@OliverCharlesworth还没听说过这个问题;这看起来确实很有趣。@OliverCharlesworth同意,访问者模式就是答案,正如你所建议的。接受另一个答案是因为它更详细,而你的答案也是正确的。@OliverCharlesworth同意,访问者模式就是答案,正如你所建议的。接受另一个答案是因为它更详细,但是你的也是正确的。Never use instanceofI不会说Never,但我同意有更好的方法:Never use instanceofI不会说Never,但我同意有更好的方法: