我知道这是一个基本问题,但我找不到其他StackOverflow帖子或任何好的API文档。
假设我有一个像Appliance
这样的抽象类,然后我有一些像Toaster
和Blender
那样扩展Appliance
的类。现在假设我要创建一个包含混合元素的ArrayList,所有这些元素最终都是Appliance
的成员,但也可以是Toaster
或Blender
。 Blender
类有一个名为turnBlenderOff()
的方法,Toaster
类有一个名为turnToasterOff()
的方法,我想迭代我的ArrayList并调用这些方法,具体取决于元素实际属于哪个子类。
目前我创建了一个名为PowerPoint
的课程并尝试:
// Constructor given an ArrayList of appliances.
public PowerPoint(ArrayList<Appliance> initial_list_of_appliances){
int listSize = initial_list_of_appliances.size();
for(int ii = 0; ii < listSize; ii++){
this.applianceList.add(initial_list_of_appliances.get(ii));
}
}
/////
// Method to switch everything in the list OFF simultaneously.
/////
public void switchOff(){
int N = this.applianceList.size();
String cur_name;
for(int ii = 0; ii < N; ii++){
cur_name = this.applianceList.get(ii).getClassName();
if(cur_name.equals("Blender")){
this.turnBlenderOff(this.applianceList.get(ii));
}
else if(cur_name.equals("Toaster")){
this.turnToasterOff(this.applianceList.get(ii));
}
else if(cur_name.equals("Oven")){
this.turnOvenOff(this.applianceList.get(ii));
}
}
}
我的大多数代码编译都很好,但我不断收到此错误消息:
PowerPoint.java:83: turnBlenderOff(appliances.ApplianceWrapper.Blender) in PowerPoint cannot be applied to (appliances.ApplianceWrapper.Appliance)
this.turnBlenderOff(this.applianceList.get(ii));
我看到这个实现仅适用于Blender
个对象的方法试图在Appliance
对象上执行,该对象碰巧是Blender
但是编译器没有意识到这一点。
我尝试在ArrayList规范中用<Appliance>
替换<? extends Appliance>
类型,但这会产生额外的错误,并且不再编译。
基于抽象类型创建列表的正确方法是什么,但是通过使用getClassName()
之类的东西来检索子类类型来调用子类型的方法?
加
由于很多人立即指出了明显的:使用继承更好,我需要解释。对于这个项目,我们必须假设Appliance
的所有子类都是由第三方人创建的,并放入一些我们无法更改的包中。这是以一种糟糕,苛刻的方式完成的,其中所有不同的子类具有不同的开/关方法,并且这不能改变。因此,设计平滑Appliance
抽象类的选项对我不开放。例如,Toaster
的方法为startToasting()
,而Oven
的方法为heatUp()
,每个方法都用作两个不同类的“on”方法。
我认为这个练习是为了教会我们:你如何改造其他人糟糕的设计工作,以尽量减少未来的伤害。
答案 0 :(得分:4)
如果要使用抽象类并且所有子类实际上具有相同的功能,为什么不在抽象基类中使用一个名为&#34; turnDeviceOff&#34;并相应地在子类中覆盖它。这是OO方法。
答案 1 :(得分:2)
使用instanceof
或getClass
,而不是滚动自己的getClassName
,然后对您刚识别的类型进行显式转换。
这就是说,更喜欢@ guitarflow的答案,尽管如果状态不能传递给switchOff
方法,那么这种方法可能无效。
答案 2 :(得分:2)
ArrayList没问题。
但你可以这样做:
public abstract class Appliance{
//declare an abstract method
abstract void switchOff();
}
然后
public class Toaster extends Appliance{
//implement the abstract method
void switchOff(){
//do toaster switchOff
}
}
对于其他子类,也要这样做。
最后,
for(Appliance element: yourList){
element.switchOff();
}
答案 3 :(得分:1)
有两种方法可以做到这一点。第一种(以及不太推荐的)方式是使用 instanceof 关键字并将设备实例投射到 Blender 实例:
for(Appliance a : list){
if(a instanceof Blender) this.turnBlenderOff((Blender)a);
...
}
这很糟糕,因为 instanceof 很慢,并且不允许您利用Java最强大的多态性,后期绑定。更好的方法是让Appliance类有一个名为 turnOff()的抽象公共方法。然后你可以做类似的事情:
for(Appliance a : list){
a.turnOff();
...
}
答案 4 :(得分:0)
你宁愿打败继承点:Appliance
应该定义一个turnOff()
方法,它的孩子应该根据自己的需要适当地实施。{1}}方法。这样你就可以使用Appliance
列表,而不必担心它是什么。
否则,他们首先扩展Appliance
的重点是什么?
如果你确实需要找出某些东西的类型,请使用instanceof
,将类名称作为字符串测试是一种非常脆弱的方法。
随机样式提示:几乎不再需要在List
s上使用索引访问权限了:
public PowerPoint(List<Appliance> initialList){
for(Appliance app : initialList)
applianceList.add(app);
}
当然还有:
applianceList.addAll(initialList);
更直接的翻译:
public void switchOff(){
for(Appliance app : applianceList)
switchOff(app);
}
private void switchOff(Appliance app){
if(app instanceof Blender)
turnBlenderOff(app);
else if(app instanceof Toaster)
turnToasterOff(app);
else if(app instanceof Oven)
turnOvenOff(app);
else
throw new RuntimeException("unknown appliance: " + app);
}
您还可以在规范化API的不同设备类周围添加一个包装器,但它可能不值得(取决于它的参与程度)。
答案 5 :(得分:0)
正确的解决方法是在Appliance类中添加turnOff()
方法,并让各种子类适当地覆盖它们。如果你这样做,那么大的“if”代码就会消失。