【问题标题】:List<Super Class> which contains objects of different subclassesList<Super Class> 包含不同子类的对象
【发布时间】:2019-07-21 23:03:58
【问题描述】:

现在我有了一个 Animal 类,并有三个子类对其进行了扩展:Dog、Cat 和 Fish。

class Dog extends Animal {
    public void bark(){}
}
class Cat extends Animal {
    public void catchMouse(){}
}
class Fish extends Animal {
    public void swim(){}
}

我有一个清单:

List<Animal> listOfAnimals = new ArrayList<>();

然后我使用静态方法将 Dog、Cat 和 Fish 的对象添加到列表中:

public static void addAnimal(List<Animal> list, AnimalInfo info) {
    Animal animal = new Animal();
    switch (info) {
        case 0:
            animal = new Dog();
            break;
        case 1:
            animal = new Cat();
            break;
        case 2:
            animal = new Fish();
            break;
    }
    list.add(animal);
}

我调用了这个静态方法 3 次,并依次将一个 Dog 对象、一个 Cat 对象和一个 Fish 对象添加到列表中。现在列表应该包含一个 Dog 实例、一个 Cat 实例和一个 Fish 实例。

现在我想在 Dog 实例上调用 bark():

list.get(0).bark();

但这显然行不通。

实现这一目标的最佳方法是什么?使用:

(Dog)(list.get(0)).bark();

【问题讨论】:

  • 是的,就是这样做的。如果您不知道它是否是Dog,您可以与animal instanceof Dog 联系。
  • 您似乎错误地处理了这个...您有一个 Animal 列表,并且您假设位置 0 是 Dog ?教科书示例有Animal.makeNoise()Dog 将覆盖 makeNoise() 并使其“吠叫”。 Cat 将覆盖并使其“喵”等。重点是,您不必对列表中的哪个动物做出假设。
  • 在我的设计中 makeNoise() 并不优雅,我想在获取时检查列表中对象的类型。那么看起来使用instanceof()是最常用的方式吗?
  • @Cyan 你可能问错了问题。标准的 OO 技术不优雅,你想做什么?
  • 那或者你可以在Animalbool canMakeNoise()中添加一个抽象方法在尝试makeNoise()之前调用

标签: java


【解决方案1】:

我认为在这种情况下使用继承可能是一个好方法,但我想我会分享一个替代方案。

这里的另一种方法是使用访问者模式。当您在声明对象的类时不知道需要对对象做什么时,或者如果您有想要与动物类分开的上下文行为(例如更新另一个对象的状态),这尤其有用(分离关注)

abstract class Animal {
    abstract void accept(AnimalVisitor visitor);
}
class Dog extends Animal {
    void bark() { ... }

    @Override
    void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }
}
class Cat extends Animal {
    void meow() { ... }

    @Override
    void accept(AnimalVisitor visitor) {
        visitor.visit(this);
    }
}

interface AnimalVisitor {
    void visit(Dog dog);
    void visit(Cat cat);
}

// Somewhere else...

AnimalVisitor voiceVisitor = new AnimalVisitor() {
    @Override
    void visit(Dog dog) {
        dog.bark();
    }

    @Override
    void visit(Cat cat) {
        cat.meow();
    }
}

animalList.get(0).accept(voiceVisitor);

【讨论】:

    【解决方案2】:

    实现这一目标的最佳方法是什么?

    如果您使用动物列表,则应在程序逻辑中进一步使用仅动物方法。 List 的想法是,您可以对其进行迭代,并将其应用于项目。 制作:

     class Animal {
          public void voice(){}
          public void swim(){}
        }
    
    class Dog extends Animal {
        public void voice(){
              print('bark')}
    
    }
    class Cat extends Animal {
        public void voice(){
              print('meow')}
    }
    

    如果你确实想要一份所有动物的清单,他们只是做笔记,而不是调用更昂贵的实例

    【讨论】:

    • 甚至保留Dog.bark(),但从voice()调用它,而不是尝试直接调用它。
    • 如果您想进行不必要的方法调用,请保留
    • 这是否意味着在Animal中保留voice()和swim()是OO设计中的一种常见方式,尽管某些子类不能voice()或swim()?
    • OO 中的一种常见方式是 Lipskow (?) 替换原则,这意味着子类必须能够做父类能够做的事情,并且以与父类相同的方式行事。这里的层次结构类似于 Animal -> (SwimmingAnimal , NotSwimmingAnimal); NotSwimmingAnimal ->(MouseCatcher, Barker) 并且有 SwimmingAnimal 的列表,无论如何。或者你可以做访客
    【解决方案3】:

    另一种方法是通过接口

        interface IAnimal {
        public void Roar();
        public void Swim();
    }
    
    class Animal implements IAnimal{
    
        @Override
        public void Roar() {
            // TODO Auto-generated method stub
            System.out.println("IROAR");
        }
    
        @Override
        public void Swim() {
            // TODO Auto-generated method stub
            if (!(this instanceof Lion)) {
    
                System.out.println("ISWIM");
            }
            else {
                System.out.println("this animal cannot swim");
            }
        }
    }
    

    请注意,我为 Swim() 方法添加了对 Lion 实例的检查,因为并非所有动物都可以游泳。

    客户代码:

        List<IAnimal> animals = new ArrayList<>();
        animals.add(new Lion());
        animals.add(new Dog());
        animals.get(0).Swim();
        animals.get(1).Roar();
    

    【讨论】:

    • 这看起来违反了开闭原则。例如,如果我添加一个新的子类 Camel(显然不会游泳),那么我需要更改我的基类以确保没有人试图将它扔进水中。
    • 你是对的。我只是让 OP 知道其他方法并摆脱烦人的演员表。当然,对于他的特殊情况,这不是正确的面向对象设计方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 2018-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多