【问题标题】:Best way to route method calling from List in Java从 Java 中的 List 路由方法调用的最佳方法
【发布时间】:2017-07-04 17:11:51
【问题描述】:

根据 List 中的不同对象类型调用类的不同方法的最佳方法绝对是什么?

常见例子:

public class Dog extends Animals{
  ...
  public void say(){
    System.out("i'm a dog");
}

public class Cat extends Animals{
  ...
  public void say(){
    System.out("i'm a cat");
}

public class OtherClass {
  public void route(){
    List<Animals> aList = new ArrayList<>();
    a.add(new Dog());
    a.add(new Cat());
    for(Animals a:aList)
     methodOverloaded(a); ---> that's the point <---
  }
  public methodOverloaded(Dog d){
    d.say();
  }

  public methodOverloaded(Cat c){
    c.say();
 }
}

当然,隐喻的目标是在第一次迭代时打印I'm a dog,在第二次运行时打印I'm a cat methodOverloaded()

我试过了

  • 访客模式
  • instanceOf()

但我正在寻找更好的解决方案。

编辑:我非常想调用示例OtherClass 的重载方法。

【问题讨论】:

  • 请阅读我的更新答案,也许现在会更清楚。
  • 非常感谢

标签: java polymorphism overloading instanceof visitor-pattern


【解决方案1】:

最好的方法是在 Animal 中定义抽象方法并在子类中覆盖它。这就是多态性的工作原理。

不需要重载。

public abstract class Animal {
    public abstract void say();
}

public class Dog extends Animal {

    @Override
    public void say() {
        System.out.println("Bark");
    }
}

public class Cat extends Animal {

    @Override
    public void say() {
        System.out.println("Meow");
    }
}

用法

public class Main() {
    public static void main(String[] args) {
        List<Animals> aList = new ArrayList<>();
        a.add(new Dog());
        a.add(new Cat());
        for (Animals a : aList)
            a.say();
    }
}

输出

Bark
Meow

____UPDATE_1

我想补充一些 cmets 为什么重载这些方法不是一个好主意。

如果您将以下内容添加到您的代码中 - 它将编译:

public methodOverloaded(Animal a) {
    a.say();
}

但它不会像您期望的那样工作。它将为List 的所有元素调用public methodOverloaded(Animal a)

为什么会这样?

对于循环的所有迭代,参数的编译时类型为Animal。每次迭代的运行时类型都不一样,但这不影响重载的选择。因为参数的编译时类型是Animal,所以唯一适用的重载是第三个。

这个程序的行为是违反直觉的,因为重载方法之间的选择是静态的,而重载方法之间的选择是动态的。

根据调用方法的对象的运行时类型,在运行时选择被覆盖方法的正确版本。

这可以通过以下方式解决:

public methodOverloaded(Animal a) {
    if (a instanceof Cat) ? "Meow" : 
       (a instanceof Dog) ? "Bark" : "Unknown Animal"
}

当然,建议的带有覆盖方法的选项展示了更好的方法和更简洁的代码。

此外,一个安全、保守的策略是永远不要导出两个重载 相同数量的参数,因为它会混淆 API 的客户端。你总是可以给方法不同的名字而不是重载它们。

但存在这样一种情况,即每对重载中至少有一个对应的形参在两个重载中具有“完全不同”(显然不可能将任一类型的实例强制转换为另一种)类型。

例如,ArrayList 有一个采用int 的构造函数和另一个采用Collection 的构造函数。很难想象在任何情况下都会调用这两个构造函数中的哪一个。

【讨论】:

  • 好的,但是如果我非常想调用示例的 OtherClass 的重载方法怎么办?
  • 你可以做到,但这不是设计课程的好方法。您的两种方法都可以合二为一,因为它们在同一个抽象上共享逻辑。依赖倒置原则鼓励使用抽象。如果您有其他目标,请描述它,我会尽力提供帮助。在您的示例中,重载这两个方法没有意义。
  • 满足您要求的最佳实践是在 java 中使用工厂设计模式。
  • @tommasoballardini 请阅读工厂设计模式,你会得到答案。
【解决方案2】:

你需要在Animal中定义方法并使其抽象

abstract class Animal {
    public abstract void say();
}

这样,您可以在 Animal 的每个子项中重写此方法,您所要做的就是a.say() 每个对象都会调用它们各自的方法。

【讨论】:

    【解决方案3】:

    你可以这样做:

    for(Animals a:aList){
        if(a instanceof Dog){
            methodOverloaded((Dog) a);
        }else if(a instanceof Cat){
            methodOverloaded((Cat) a);
        }
    }
    

    但是根据你在问题中描述的场景,@J-Alex 的回答是一个不错的方法。

    【讨论】:

      【解决方案4】:

      我可以在这里向您展示“工厂设计模式”如何是合适的方式。 定义你的主类,如:

      public abstract class Animal {
          public abstract void say();
      }
      
      
      public class Dog extends Animal {
      
          @Override
          public void say() {
              System.out.println("Bark");
          }
      }
      public class Cat extends Animal {
      
          @Override
          public void say() {
              System.out.println("Meow");
          }
      }
      
      public class FactoryClass{
      
      public static Animal getCorrectAnimal(String type){
       if("Cat".equalsIgnoreCase(type)) return new Cat();
       else if ("Dog".equalsIgnoreCase(type)) return new Dog();
      
      return null;
      }
      
      }
      
      
      public class TestFactoryClass {
      
          public static void main(String[] args) {
              Animal an = ComputerFactory.getCorrectAnimal("Cat");
              List<Animals> aList = new ArrayList<>();
              a.add(FactoryClass.getCorrectAnimal("Dog"));
              a.add(FactoryClass.getCorrectAnimal("Cat"));
              for (Animals a : aList)
                  a.say();
          }
      
          }
      
      }
      

      相信我,如果你在这里分析抽象的级别,那就太棒了。 客户/消费者不必知道 Dog 或 Cat 类,他/她只需要知道类型和一般抽象类 Animal。 如果您使用更高的类型,您甚至可以在此处摆脱类型抽象级别;您可以为此阅读“抽象工厂设计”。通过这种方式,您公开了最少的类功能(就像这里您通过在主类中直接使用它们来公开 Dog 和 Cat 类)。满意请点赞。

      【讨论】:

      • @tommasoballardini 这里也不需要强制转换或直接使用类。
      • Factory 是Creational Design模式,这里的“路由”机制仍然是多态。您做了同样的事情,但使用了另一种对象创建方法。如果你有一个公共构造函数,Factory 没有意义。不要混淆应用程序不同组件的职责。
      • @J-Alex 这就是为什么它是最好的方法之一。请尝试理解 tommasoballardini 提出的问题。你不同意这是更好的方法吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多