需要以下背景:这种编码方式的目的是避免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;
}
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();
}
现在问题; execute(C)方法永远不会被执行,它总是执行(A)方法。我怎么解决这个问题?我无法更改执行(A)方法的签名以执行(B),因为这会产生错误,说java"无法解析方法execute(A)"在MyClass#doSomething。
答案 0 :(得分:5)
方法重载在编译时解决。在编译时,m
的类型为A
,因此execute(A m)
会被执行。
此外,私有方法不可覆盖。
解决方案是使用@OliverCharlesworth建议的访客模式。
答案 1 :(得分:3)
您的代码说明了对象的静态和动态类型之间的区别。静态类型是编译器已知的;动态类型是运行时实际存在的东西。
model
字段的静态类型为A
:
private final A model;
也就是说,编译器知道A
本身或其某些的实现将被分配给model
。编译器不知道任何其他内容,因此在execute(A m)
和execute(C m)
之间进行选择时,唯一的选择是execute(A m)
。该方法是在对象的静态类型上解决的。
instanceof
了解动态类型。它可以告诉我model
设置为C
,因此会在您的打印输出中报告true
。
您可以通过向A
添加方法并在B
和C
覆盖该方法来解决问题,以便路由到正确的execute
:
abstract class A {
public abstract void callExecute(MyClass back);
}
class B extends A {
public void callExecute(MyClass back) {
back.execute(this);
}
}
class C extends A {
public void callExecute(MyClass back) {
back.execute(this);
}
}
class MyClass {
private final A model;
public MyClass(A m) {
this.model = m;
}
public void doSomething() {
System.out.println(this.model instanceof C); //TRUE!!
model.callExecute(this.model);
}
public void execute(B m) {
System.out.println("noo");
}
public void execute(C m) {
System.out.println("yay");
}
}
请注意,两个实现都会调用
back.execute(this);
但是,B
中的实现具有this
类型的B
,而C
内的实现具有类型this
的{{1}},所以调用被路由到C
的{{1}}方法的不同重载。
我无法将
execute
方法的签名更改为MyClass
另请注意,现在您也可以(也应该)这样做,因为根据execute(A)
的类型对正确的重载执行回调。
答案 2 :(得分:2)
方法重载是编译时多态。因此,对于调用方法execute(C)
,您需要将模型定义为class C
。
最好在execute()
中定义方法class A
并在子类中覆盖它。
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检查
答案 3 :(得分:-2)
您正在构造函数中发送类型C的对象作为类型A的对象(您已完成向上转换)并将其分配给类型A的引用(这将导致仅调用execute(A)方法)。您可以检查对象是否是C的实例,并根据结果调用所需的方法。你可以这样做
public void doSomething(){
System.out.println(model instanceof C);
if (model instanceof C) execute((C)model);
else
execute(model);
}