3

我有一个抽象类Animal,有两个扩展类,DogCat.

public abstract class Animal {
...
}
public class Dog extends Animal {
...
}
public class Cat extends Animal {
...
}

在另一个类中,我有一个ArrayList<Animal> animals包含Cat和的实例Dog

在带有 的类中ArrayList<Animal>,我希望能够使用或作为参数重载方法doSomething()Dog dCat c仍然能够从 Animals 的 ArrayList 中调用它们。如下:

public void aMethod(){
    ArrayList<Animals> animals = getTheAnimals();
    for (Animal a : animals){
        doSomething(a);
    }
}
public void doSomething(Dog a){
//a is an object of type dog
}
public void doSomething(Cat a){
//a is an object of type cat
}

从本质上讲,每种方法都充当type该方法接收动物的“选择器”。

如上所述尝试时,出现以下编译错误:

错误:找不到适合 doSomething(Animal) 的方法

我知道我可以使用类似的东西instanceOfor a.getClass().equals(type),但我已经读到这是不好的做法。这个问题与其他问题略有不同,因为我想要两个单独的方法,每个方法都有不同的参数。

编辑:我将避免使用访问者模式,因为我认为它不太适合我的实际实现。将 doSomething() 方法移动到每个类中,并进行重构以确保这具有逻辑意义(每个类都执行它应该负责的操作)。谢谢。

4

5 回答 5

2

与其在客户端方法中迭代并调用 doSomething(),不如将 doSomething() 保留为抽象类中的方法,这样您就可以在 Cat 或 Dog 中更改您想要的方式。

public void aMethod(){
  ArrayList<Animals> animals = getTheAnimals();
    for (Animal a : animals){
       a.doSomething();
     }
  }

abstract Animal {
   doSomething();
}

Dog extends Animal {


  doSomething() {
    //Dog stuff
  }

}

Cat extends Animal {

   doSomething() {
     //cat stuff
   }

}
于 2018-08-23T16:13:25.783 回答
2

将方法移动到Abstract类中,因此每个实现都必须制作自己的方法版本。

public abstract class Animal {

  public abstract void doSomething();
}

public class Dog extends Animal {
   public void doSomething(){
       System.out.println("Bark");
   }
}

public class Cat extends Animal {
   public void doSomething(){
       System.out.println("Meow");
   }
}

然后在您上面提到的另一个类中,您可以执行以下操作:

  public void vocalize(){
      ArrayList<Animals> animals = getTheAnimals();
      for (Animal a : animals){
         a.doSomething();
      }
   }
于 2018-08-23T16:13:28.917 回答
1

Java 中有一条重要规则定义了继承、接口和抽象类的大部分工作方式——超类引用变量可以包含子类对象。因此,有几种方法可以克服您的错误。

  1. 如果您的DogCat类是Animal抽象类的具体类,那么您可以简单地定义一个doSomething(Animal animal)并且该方法将起作用。
  2. 您可以尝试使您的doSomething方法通用。如 public <T> void doSomething(<T extends Animal> animal)
  3. 或者您可以在 Animal 中定义一个 doSomething 抽象方法,并让子类为其提供自己的实现,正如 Andrew S. 在上述答案中所建议的那样。
于 2021-09-28T02:18:54.690 回答
1

您需要双重调度:java 提供的运行时类型 + 您必须实现的运行时参数类型。访问者模式是一种方法。

引入一个Visitable接口,实现它Animal以使其接受访问者。
然后介绍一位访客。您可以根据您的要求使用或不使用接口来实现它。
保留代码实际逻辑的简单版本(通过将访问者耦合到当前类):

public interface Visitable{
   void accept(MyClassWithSomeAnimals myClassWithSomeAnimals);
}

public abstract class Animal implements Visitable{
...
}

public class Dog extends Animal {
...
  void accept(MyClassWithSomeAnimals myClassWithSomeAnimals){
     myClassWithSomeAnimals.doSomething(this);
  }

}
public class Cat extends Animal {
...
  void accept(MyClassWithSomeAnimals myClassWithSomeAnimals){
     myClassWithSomeAnimals.doSomething(this);
  }
}

MyClassWithSomeAnimals以这种方式更新:

public void aMethod(){
    ArrayList<Animals> animals = getTheAnimals();
    for (Animal a : animals){
         a.accept(this);
    }
}

该解决方案有效,但有一个缺点。它暴露MyClassWithSomeAnimalsAnimal子类,而这些只需要有一种方法来调用访问者方法,而不是任何公共方法。
所以一个改进是引入一个Visitor接口来限制doSomething()Animal 对访问者的了解:

public interface Visitor{
  void doSomething(Dog dog);
  void doSomething(Cat cat);
}

因此,MyClassWithSomeAnimals请实现它并更改可访问类/具体类以使用它:

public interface Visitable{
  void accept(Visitor visitor);
}

public class Dog extends Animal {
...
  void accept(Visitor visitor){
     visitor.doSomething(this);
  }

}
public class Cat extends Animal {
...
  void accept(Visitor visitor){
     visitor.doSomething(this);
  }
}
于 2018-08-23T16:57:53.803 回答
1

是的 instanceof 和 getclass 会使您的类依赖于特定类型,并且使用这种做法并不是很好。

但是通过让方法接受Dog或者Cat你最终取决于具体的具体实现。

更好的方法是在Animal接口中定义方法performAction()Dog在和中提供实现Cat。现在迭代您的动物列表,并performAction()根据实际实例调用和多态地调用 Dog 或 Cat 实现的方法。稍后即使在代码中添加了另一个 Animal 实现,您也无需修改代码。

这样,您将在 Dog 类中拥有与 Dog 相关的逻辑,在 Cat 类中拥有与 Cat 相关的逻辑,而不是在任何外部不同的类中。这将有助于尊重 OCP 和 SRP(开放式关闭和单一职责原则)。

此外,如果您希望稍后执行行为并且您的情况是您知道您的实现是固定的并且希望使用代码稍后注入行为,那么我会推荐Visitor模式,但我仍然认为这不应该被滥用,(设计模式是通常不使用但被滥用)有时我们在不需要时强制使用模式。访问者模式使用多态将第一个调用分派到特定实例,第二个分派从该实例调用实现访问者接口的类中的重载方法。(名称可以不同,我提到了访问者来暗示实现访问者概念的类)。仅在需要时才使用类似访问者模式的实现,并且以上基于多态的正常方法不适合这种情况。

于 2018-08-23T17:28:25.153 回答