Java 接口不考虑来自 super class 的方法实现

Java Interface does not take into consideration method implementation from the super class

以下鸭子示例基于 Head First 设计模式一书。

我有一个游戏,有不同类型的鸭子。有一只超级class Duck,它有两种行为:fly 和quack,存储在字段中。具体 classes 决定(在构造函数中)具体品种具有哪种行为(参见 MalardDuck class)。

我意识到我希望我的鸭子不仅具有 Duck 类型,而且还具有 Quackable 类型(这样我就可以拥有只接受嘎嘎类型的方法 - 假设还有其他类型的嘎嘎 - 请参阅 Lake class)。当我在 MallardDuck 中实现接口时,编译器抱怨 class 没有 quack 方法,尽管它在其 superclass - Duck class.

中定义

现在我想到了两个解决方案:

  1. 通过从 superclass 调用方法来覆盖方法 quack: super.quack() <- 但这似乎是不必要的(理论上)- a child class 可以直接访问 superclass 的 public 方法,那么为什么接口 Quackable 甚至抱怨...?
  2. 让 Duck 实现 Quackable -> 这是相当不合逻辑的,因为有些 Ducks 不嘎嘎(它们的 quackBehaviour 是用 SiletnQuack class 实现的)。

但是在这两种解决方案中,鸭子:

这不是根本错误吗?我错过了什么?

abstract class Duck{
    protected Flyiable flyBehaviour;
    protected Quackable quackBehaviour;
    public void quack(){
        quackBehaviour.quack();
    }
    void performFly(){
        flyBehaviour.fly();
    }
    void swim(){
        // swimming implementation
    }
    abstract void display();
}
interface Quackable{
    void quack();
}
class Quack implements Quackable{

    @Override
    public void quack() {
        System.out.println("Quack!");
    }
}
class Quack implements Quackable{

    @Override
    public void quack() {
        System.out.println("Quack!");
    }
}
class SilentQuack implements Quackable{

    @Override
    public void quack() {
        System.out.println("...");
    }
}
class MallardDuck extends Duck{
    public MallardDuck(){
        quackBehaviour = new Quack();
        flyBehaviour = new FlyWithWings();
    }

    @Override
    void display() {

        // it looks like a Mallard duck
    }
}

如果我想接受这种方法的鸭子作为嘎嘎(以及其他动物)怎么办:


class Lake{
    ArrayList<Quackable> quackingAnimals;
    void addQuackingAnimal(Quackable animal){
        quackingAnimals.add(animal);
    }
    void displayQuackables(){
        //...
    }
}

解决方案 1:

class MallardDuck extends Duck implements Quackable {
    public MallardDuck(){
        quackBehaviour = new Quack();
        flyBehaviour = new FlyWithWings();
    }

    @Override
    void display() {

        // it looks like a Mallard duck
    }

    @Override
    public void quack() {
        super.quack();
    }
}

解决方案 2:

abstract class Duck implements Quackable{
    protected Flyiable flyBehaviour;
    protected Quackable quackBehaviour;
    public void quack(){
        quackBehaviour.quack();
    }
    void performFly(){
        flyBehaviour.fly();
    }
    void swim(){
        // swimming implementation
    }
    abstract void display();
}

这是因为 Duck class 没有实现 Quackable 接口中的 quack 方法。虽然它有一个具有相同签名的 quack 方法,但它不是在接口中声明的相同方法。

我不明白,为什么解决方案 2(使 Duck class 实现 Quackable 接口)是不合逻辑的 - 它确实暴露了 public 嘎嘎的方法,所以它的所有后代无论如何都会 quack (但是在 Quackable 接口中声明的 quack 不同)。在我看来(唯一的意见),Duck class 应该实现 Quackable.

如果(在你的情况下)不是所有 Ducks quack,那么 Duck 不能被视为具有 Quackable 的东西是合理的行为,因此无法将其添加到 Quackable 个对象的集合中。在这种情况下(在我看来)你可以创建另一个抽象 class 扩展 Duck 并实现 Quackable 接口(如 QuackingDuck),在这种情况下(在我看来)你应该从 Duck class 中删除 quack 方法 - 因为并非所有 Ducks quack.

我希望它能回答您的问题。总结:

  • Duck 中的 quack 方法不是 Quackable 接口中 quack 方法的实现(例如 [=61= 中的情况) ])
  • 在当前的实现中,所有 Ducks quack,只有它们的 quacking 行为(实现)不同(并且在 SilentDuck 的情况下 - 它仍然 quacks)

[在你的问题中,我更喜欢使用“委托”一词而不是“行为”,因为我个人认为它更能表达这个概念。]

您的界面 Quackable 实际上意味着“可以嘎嘎叫的东西”。

甚至原始的 Duck 抽象 class 也有那个方法,所以它已经遵循那个接口,但是你“忘记”声明了那个事实。在一致的世界中,您应该 Duck 从一开始就实施 Quackable

关于 HAS 和 IS:

所有 Ducks 都有一些 quack() 方法,因此实际上它们是 Quackable(意思是“可以嘎嘎叫的东西”)。他们如何决定他们的 quack() 实现、使用继承的委托方法、通过提供不同的 delegete/behaviour 修改继承的版本或覆盖该方法都无关紧要。

他们拥有 quack() 能力这一事实将他们定义为 Quackable

你的 Duck subclasses 也有一个 Quackable 并将其用作代表,将真正的嘎嘎声推迟到其他一些“行为”实例(比如不发表意见的政客他自己,而是通过代言人)。

因此,class 同时成为 Quackable 和拥有 Quackable 并没有错。在现实世界中,我们都是 LivingCreatures,我们中的许多人都将 LivingCreatures 作为我们的宠物。

关于行为对象的备注:

尽管此模式可能很有用,但请注意,您失去了使 quack() 行为取决于鸭子状态的能力(或至少使其相当复杂)(例如,如果在水上或在空中)。

您在 superclass 和接口中使用相同的方法。 MallardDuck 中的 quack() 方法 class 一次做 2 件事:

  1. 在 superclass Duck
  2. 中覆盖 quack()
  3. 在 Quackable 接口中实现 quack()

解决方案 2 可帮助您避免这种情况。