从 Java 中的 List 路由方法调用的最佳方式

Best way to route method calling from List in Java

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

常见例子:

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().

我试过了

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

编辑:我非常想调用示例 OtherClass.

的重载方法

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

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

这样,你就可以在每个child的Animal中重写这个方法了,你所要做的就是a.say() 每个 object 将调用各自的方法。

最好的方法是在 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

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

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

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 的构造函数。很难想象在任何情况下都会调用这两个构造函数中的哪一个。

你可以这样做:

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

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

我可以在这里向您展示 "Factory Design pattern" 是怎样一种合适的方式。 定义你的 main class 如:

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();
    }

    }

}

相信我,如果您在这里分析抽象级别,那就太棒了。 client/consumer 永远不必知道狗或猫 class,he/she 只需要知道类型和一般摘要 class 动物。 如果你使用更高层次的抽象,你甚至可以在这里去掉类型;您可以为此阅读 "Abstract Factory Design"。通过这种方式,您暴露了最少的 class 特性(就像这里您暴露了 Dog 和 Cat class 一样,通过在 main class 中直接使用 new 来暴露它们)。如果您满意,请点赞。