如何调用列表中某些对象实现的特定接口? Java

How do I call specific interfaces that are implemented by some objects in a List? Java

假设我有一只基本动物class

abstract class Animal {
// basic animal code
}

现在我有 2 种不同的动物...

public class Dog extends Animal{
// dog code
}

public class Bird extends Animal implements Flyable{

    // bird code
    @Override
    public void fly() {
        System.out.println("flap flap");
    }

}

Flyable 是一个包含单一方法的简单接口:

public void fly();

如果我有一个动物列表并且我想遍历它,告诉鸟儿飞但让狗独自一人,我该如何实现这个?

public class Test {

    public static List<Animal> animals = new ArrayList<Animal>();

    public static void main(String[] args) {
        animals.add(new Bird("flop"));
        animals.add(new Dog("plop"));

        for(Fly f : animals) { // exception here because of mismatch of types
            f.flap();
        }

    }

}

到目前为止我发现的唯一选择是使用 instanceof 来确定 class 是否实现了 Flyable 接口,但是快速 google 搜索表明这对业务不利。 来源例如: https://www.quora.com/Is-using-instanceof-in-Java-consider-bad-practice-Any-alternative-to-using-this-keyword 将 instanceof 的使用视为糟糕的设计。

我觉得有一种我以前见过的直观方法可以做到这一点,但找不到好的解决方案。

Flyable is a simple interface that holds a single method:

public void fly();

我想这是一个打字错误,因为您调用的方法被命名为 flap 而不是 fly.

您可以通过使用 instanceof 关键字检查 class is-a superclass.

来解决问题
for(Animal animal : animals) { // loop through all animals
    if(animal instanceof Flyable) { // if that animal IS-A Flyable (so it can fly)
        ((Flyable) animal).flap(); // cast to Flyable and let it fly!
    }
}

The only option I have found so far is using instanceof to determine whether a class implements the Flyable interface, but a quick google search suggests this is bad for business

在我看来一点也不差。这是完成任务的唯一方法。

当您在 Animal class 中实现包含 fly() 方法声明的 Flyable 接口时,您只需定义每个子 class 的动物有飞行能力。

在我看来,使用 instanceof 是一种不好的做法,因为它会使代码非常混乱:一方面 Dog 有一个 fly() 实现(它通过 Animal 间接实现 Flyable 接口class),另一方面,在接口实例上调用 fly() 时不会调用它。

你至少有 2 种方法可以防止 Dog 拥有飞行能力,这是我最喜欢的两种方法:

  1. 您可以创建 2 个 classes,FlyingAnimalNonFlyingAnimal,它们都扩展 Animal class 而 FlyingAnimal class 实现了 Flyable 接口而 NonFlyingAnimal 没有。 鸟将扩展 FlyingAnimal class 而狗将扩展 NonFlyingAnimal class。 通过这种方式,您可以创建一个 FlyingAnimal 列表,迭代它,并对它的每一个飞行成员调用 fly() 方法(狗不是其中之一)。

  2. 使用策略设计模式:

public interface Flyable {
    String fly();
}

class ItFlys implements Flyable {
    public String fly() {
        return "I can fly";
    }
}

class CantFly implements Flyable {
    public String fly() {
        return "I can't fly";
    }
}

public class Animal {

    private String name;
    private double height;
    private int weight;
    private String favFood;
    private double speed;
    private String sound;

    public Flyable flyingType;

    public String tryToFly() {
        return flyingType.fly();
    }

    public void setFlyingAbility(Flyable newFlyType) {
        flyingType = newFlyType;
    }
}

public class Bird extends Animal{
    public Bird() {
        super();
        flyingType = new ItFlys();
    }   
}

public class Dog extends Animal{
    public Dog() {
        super();
        flyingType = new CantFly();
    }   
}

这样,您就为Animal的每个子class设置了飞行类型。 当您在 Dog class 上调用 fly() 方法时,您将获得 "non-flying animal" 行为。

一种替代方法是组织您的对象,这样您就不必检查每个对象来确定如何处理它。例如,您可以维护一个 Kingdom class 和 Animal 的各种集合,包括 Flyable 集合。迭代 Flyable 集合不需要测试每个实例是否是 Flyable。如果您有其他 classes 仅在 Flyable 对象上运行,它们也不必测试每个成员,从而导致代码更清晰,工作更少。

您可以使用以下几种可能性:

  1. fly()作为抽象方法放在基class中。使 Dog 的实现抛出 CannotFlyException,或者以其他方式实现某些 "non-flying" 行为。然后使用

    遍历你的 List<Animal>
    try {
        animal.fly();
    catch (CannotFlyException() cfe) {
        System.out.println("grounded!");
    }
    
  2. 为您的 Animal() class 提供一个列出支持的操作的抽象方法,然后测试每个成员以查看它是否实现了 fly() 方法:

    public abstract class Animal {
        private Set<String> behaviors;
    
        public Animal() {
            behaviors = new HashSet<String>();
        }
    
        public Set<String> getBehaviors() {
            return behaviors;
        }
    }
    
    public class Dog extends Animal {
        public Dog() {
            super();
            behaviors.add("fetch");
        }
    
        public String fetch(String fetched) {
            return "Dog fetched " + fetched;
        }     
    }
    
    public class Bird extends Animal implements Flyable {
        public Dog() {
            super();
            behaviors.add("fly");
        }
    
        @Override
        public String fly() {
            return "flap flap";
        }     
    }
    
    ....
    
    List<Animal> animals = MagicalAnimalListCreator.MakeAnimalList();
    for (Animal animal : animals) {
        if (animal.getBehaviors().contains("fly")) {
            animal.fly();
        }
    }
    
  3. 你可以按照你目前的方式来做,但是在尝试飞行之前尝试将你列表中的每个成员都投射到 Bird 并抓住 ClassCastException。如果施法成功,你就会得到一只鸟并且可以飞了。