以子类作为参数的重载方法,但将方法称为超类

问题描述 投票:0回答:5

我有一个抽象类

Animal
,有两个扩展类,
Dog
Cat

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

在另一个课程中,我有一个

ArrayList<Animal> animals
,其中包含
Cat
Dog
的实例。

在带有

ArrayList<Animal>
的类中,我希望能够使用
doSomething()
Dog d
作为参数重载方法
Cat 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) 的方法

我知道我可以使用

instanceOf
a.getClass().equals(type)
之类的东西,但我读到这是不好的做法。 这个问题与另一个问题略有不同,因为我想要两个单独的方法,每个方法都有不同的参数。

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

java overloading subclass extending-classes
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
   }

}

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(){
      List<Animal> animals = getTheAnimals();
      for (Animal animal : animals){
         animal.doSomething();
      }
   }

1
投票

是的,instanceof 和 getclass 会让你的类依赖于特定的类型,使用这样的做法不太好。

但是通过接受

Dog
Cat
的方法,你最终还是会依赖于具体的具体实现。

更好的方法是在

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

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

此外,如果您希望稍后执行行为,并且您的情况是这样的,您知道您的实现是固定的,并且希望使用代码稍后注入行为,那么我会推荐

Visitor
模式,但我仍然认为这不应该被滥用, (设计模式通常不被使用而是被滥用)有时我们在不需要时强制使用模式。访问者模式使用多态将第一次调用分派到特定实例,第二次调用将调用实现访问者接口的类中的重载方法。 (名称可以不同,我提到 Visitor 是为了暗示实现访问者概念的类)。仅在需要时才采用访问者模式之类的实现,而正常的上述基于多态的方法不适合这种情况。


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

这个解决方案有效,但有一个缺点。它将

MyClassWithSomeAnimals
暴露给
Animal
子类,而这些子类只需要有一种方法来调用访问者方法,而不是任何公共方法。
因此,改进是引入一个
Visitor
界面来限制
doSomething()
Animal 对访客的了解:

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

所以让

MyClassWithSomeAnimals
实现它并更改 Visitable 类/具体类来使用它:

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

1
投票

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

  1. 如果您的
    Dog
    Cat
    类是
    Animal
    抽象类的具体类,那么您可以简单地定义一个
    doSomething(Animal animal)
    并且该方法将起作用。
  2. 您可以尝试使您的
    doSomething
    方法变得通用。例如
    public <T> void doSomething(<T extends Animal> animal)
  3. 或者您可以在 Animal 中定义 doSomething 抽象方法,并让子类为其提供自己的实现,如 Andrew S. 在上面的答案中所建议的。
© www.soinside.com 2019 - 2024. All rights reserved.