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

时间:2017-07-04 17:11:52

标签: java polymorphism overloading instanceof visitor-pattern

根据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的重载方法。

4 个答案:

答案 0 :(得分:2)

最好的方法是在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();
}

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

为什么会这样?

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

此程序的行为是违反直觉的,因为重载方法中的选择是静态的,而重写方法中的选择是动态的。

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

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

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

当然,建议使用覆盖方法的选项可以说明更好的方法和更干净的代码。

另外,安全,保守的政策永远不会导出两个超载 相同数量的参数,因为它可能会混淆API的客户端。您始终可以为方法指定不同的名称,而不是重载它们。

但是有一种情况是,每对过载中至少有一个相应的形式参数在两个过载中具有“根本不同”(当显然不可能将任何一种类型的实例转换为另一种类型时)。

例如,ArrayList有一个构造函数接受int,第二个构造函数接受Collection。很难想象在任何情况下都会对这两个构造函数中的哪一个进行混淆。

答案 1 :(得分:0)

您需要在Animal中定义方法并将其设为抽象

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

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

答案 2 :(得分:0)

你可以这样做:

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

但根据你在问题中所描述的情景,@ J-Alex回答是一个很好的方法。

答案 3 :(得分:0)

我可以告诉你“工厂设计模式”是如何适合这里的。 定义你的主要类:

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。如果你使用更高的类,你甚至可以在这里删除类型抽象程度;你可以阅读“抽象工厂设计”。通过这种方式,您可以暴露出最少的类功能(例如,您可以通过直接使用主类中的new来暴露Dog和Cat类)。如果您满意,请提前投票。