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

如何调用由列表中的某些对象实现的特定接口?JAVA,java,list,generics,interface,instanceof,Java,List,Generics,Interface,Instanceof,假设我有一门基本的动物课 abstract class Animal { // basic animal code } 现在我有两种不同的动物 public class Dog extends Animal{ // dog code } 及 Flyable是一个简单的界面,包含一个方法: public void fly(); 如果我有一张动物的清单,我想循环浏览,告诉鸟儿们要飞,但不要管狗,我该如何做到这一点 public class Test { public static L

假设我有一门基本的动物课

abstract class Animal {
// basic animal code
}
现在我有两种不同的动物

public class Dog extends Animal{
// dog code
}

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的使用视为糟糕的设计

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

Flyable是一个简单的界面,包含一个方法:

public void fly();
我认为这是一个输入错误,因为您调用的方法名为
flap
,而不是
fly

您可以通过使用
instanceof
关键字来检查类
是否为-a
超类来解决此问题

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!
    }
}

到目前为止,我找到的唯一选择是使用instanceof来确定类是否实现了Flyable接口,但快速的谷歌搜索表明这对业务不利


在我看来,这一点都不坏。这是完成任务的唯一途径。

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

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

你至少有两种方法可以阻止狗拥有飞行能力,以下是我最喜欢的两种方法:

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

  • 使用策略设计模式:

  • 这样,您可以为
    动物的每个子类设置一个飞行类型。
    
    Dog
    类上调用
    fly()
    方法时,将得到一个“非飞行动物”行为。

    另一种方法是组织对象,这样您就不必检查每个对象来确定如何处理它。例如,您可以维护一个包含各种
    动物
    集合的
    王国
    类,包括一个
    可飞行
    集合。在可飞行集合上迭代不需要测试每个实例是否可飞行。如果您有其他仅在可飞行对象上操作的类,则它们也不必测试每个成员,从而以更少的工作量生成更干净的代码。

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

  • fly()
    作为抽象方法放在基类中。使
    Dog
    的实现抛出
    CannotFlyException
    ,或者以其他方式实现一些“非飞行”行为。然后在尝试飞行之前迭代
    列表。如果强制转换成功,您就有了一只鸟,可以飞行


  • 有没有办法避免instanceof
    ?OP说“到目前为止,我找到的唯一选择是使用instanceof来确定一个类是否实现了Flyable接口,但快速的谷歌搜索表明这对业务不利。”@ChandlerBing不这么认为。如果你想调用一个特定的方法,你需要检查类型是否允许你这样做,然后进行转换。这是一个拼写错误,只是修复了它。我总是避免使用instanceof,因为每次我上网时,每个人都说它“不好”练习。但如果这是最好的方法,那么我会勉强去做,@John这一点是正确的。然而,在这种情况下,我认为使用
    instanceof
    是非常好的。即使
    是一种
    动物,它也有一些特定的行为:它会飞。因此,使用
    instanceof
    来称呼我是非常好的方法。如果你想避免它,也许你可以在
    Animal
    类中创建一个通用的
    move
    方法,它可以在所有
    Animal
    s中调用。然后,在
    Bird中,move
    可以使它摆动,而不是仅仅移动。但是一只鸟也可以只移动而不飞,所以我认为这不能解决问题。好吗这就是说,尽管我看到了很多关于
    instanceof
    为什么不好的争论,但我最终在生产代码中依赖它的次数超过了我所想的次数。这是Java内部NPE安全的方式,表示“这个对象是那个类的一员”
    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!
        }
    }
    
    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();
        }   
    }
    
    try {
        animal.fly();
    catch (CannotFlyException() cfe) {
        System.out.println("grounded!");
    }
    
    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();
        }
    }