【问题标题】:How do I call specific interfaces that are implemented by some objects in a List? Java如何调用列表中某些对象实现的特定接口?爪哇
【发布时间】:2017-05-20 10:30:06
【问题描述】:

假设我有一个基本的动物课程

abstract class Animal {
// basic animal code
}

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

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 来确定一个类是否实现了 Flyable 接口,但快速的谷歌搜索表明这对业务不利。 来源如: https://www.quora.com/Is-using-instanceof-in-Java-consider-bad-practice-Any-alternative-to-using-this-keyword 将 instanceof 的使用视为糟糕的设计。

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

【问题讨论】:

    标签: java list generics interface instanceof


    【解决方案1】:

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

    public void fly();
    

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

    您可以通过使用instanceof 关键字检查类is-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 接口,但快速谷歌搜索表明这对业务不利

    在我看来,这还不错。这是完成任务的唯一方法。

    【讨论】:

    • 有什么办法可以避免instanceof? OP 说“到目前为止我发现的唯一选择是使用 instanceof 来确定一个类是否实现了 Flyable 接口,但快速的谷歌搜索表明这对业务不利
    • @ChandlerBing 不这么认为。如果你想调用一个特定的方法,你需要检查类型是否允许你这样做,然后强制转换。
    • 这是一个错字,刚刚改正。我一直避免使用 instanceof,因为每次我上网时,每个人都说这是“糟糕”的做法。但如果这是最好的方法,那我就勉强去做,
    • @John 这一点是正确的。但是,在这种情况下,我认为使用instanceof 非常好。即使BirdAnimal,它也有一些特定的行为:它可以飞。因此使用instanceof 调用该方法是完全可以的。如果你想避免它,也许你可以在Animal类中创建一个通用的move方法,可以在所有Animals上调用。然后,在Bird,move 内,您可以让它拍动而不是仅仅移动。但是鸟也可以不飞就移动,所以我认为这不能解决问题。
    【解决方案2】:

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

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

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

    1. 您可以创建 2 个类,FlyingAnimalNonFlyingAnimal,它们都扩展了 Animal 类,而 FlyingAnimal 类实现了 Flyable 接口,而 NonFlyingAnimal 没有。 Bird 将扩展 FlyingAnimal 类,而 Dog 将扩展 NonFlyingAnimal 类。 通过这种方式,您可以创建一个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 的每个子类设置一个飞行类型。 当您在 Dog 类上调用 fly() 方法时,您将获得“非飞行动物”行为。

    【讨论】:

      【解决方案3】:

      另一种方法是组织您的对象,这样您就不必检查每个对象以确定如何处理它。例如,您可能会维护一个 Kingdom 类,其中包含 Animal 的各种集合,包括 Flyable 集合。遍历 Flyable 集合不需要测试每个实例是否是 Flyable。如果您有其他仅对 Flyable 对象进行操作的类,它们也不必测试每个成员,从而使代码更简洁,工作量更少。

      【讨论】:

        【解决方案4】:

        您可以使用几种可能性:

        1. fly() 作为抽象方法放在基类中。让Dog 的实现抛出CannotFlyException,或者以其他方式实现一些“非飞行”行为。然后使用

          遍历你的List&lt;Animal&gt;
          try {
              animal.fly();
          catch (CannotFlyException() cfe) {
              System.out.println("grounded!");
          }
          
        2. 给你的 Animal() 类提供一个抽象方法,列出支持的操作,然后测试每个成员看它是否实现了 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。如果施法成功,你就有一只鸟,可以飞了。

        【讨论】:

        • 所有这一切,尽管我已经看到了为什么instanceof 不好,但我最终在生产代码中依赖它的次数超出了我的计算范围。这是 Java 内部 NPE 安全的说法,即“该对象是该类的成员”。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-11
        • 2011-04-12
        • 1970-01-01
        • 2014-01-13
        • 1970-01-01
        • 2011-01-07
        相关资源
        最近更新 更多