Java ArrayList由一个抽象类和任何扩展它的东西组成?

时间:2012-02-08 22:42:11

标签: java arraylist

我知道这是一个基本问题,但我找不到其他StackOverflow帖子或任何好的API文档。

假设我有一个像Appliance这样的抽象类,然后我有一些像ToasterBlender那样扩展Appliance的类。现在假设我要创建一个包含混合元素的ArrayList,所有这些元素最终都是Appliance的成员,但也可以是ToasterBlenderBlender类有一个名为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”方法。

我认为这个练习是为了教会我们:你如何改造其他人糟糕的设计工作,以尽量减少未来的伤害。

6 个答案:

答案 0 :(得分:4)

如果要使用抽象类并且所有子类实际上具有相同的功能,为什么不在抽象基类中使用一个名为&#34; turnDeviceOff&#34;并相应地在子类中覆盖它。这是OO方法。

答案 1 :(得分:2)

使用instanceofgetClass,而不是滚动自己的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);

编辑2

更直接的翻译:

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”代码就会消失。