同时调用类似的超类和子类方法

时间:2017-03-20 12:37:18

标签: java

我正在创建一个程序,其中我有一个Person子类和一个Animal超类。我想知道在两者中同时调用类似方法的最佳方法是什么。 例如,一种方法是:

public abstract class Animal {
    public void walk() {
        // Do stuff
        childWalk();
    }
    public abstract void childWalk();
}
public class Person extends Animal {
    @Override
    public void childWalk() {
        // Do stuff
    }
}

这样,我可以调用person.walk()并同时调用child和parent方法,并在两者中运行代码。 我考虑的另一种方式是:

public abstract class Animal {
    public void walk() {
        // Do stuff
    }
}
public class Person extends Animal {
    @Override
    public void walk() {
        super.walk();
        // Do stuff
    }
}

这似乎更好,仍然称为person.walk()然而它仍然不理想,我不禁觉得有一种更有效的方法,我没有想到这样做。让子类知道父类的方法是否被调用是错误的。 (无论如何,应始终调用Animal.walk()) 也许我会以错误的方式解决它,我根本不需要这样做?我希望能够做到这一点的原因是因为每个Animal都需要在行走时做事,但有时候一个人想要添加到这个行为,可能会为每个步骤增加一个变量,或者什么。

也许这个问题很愚蠢,但我一直在谷歌搜索一段时间,看不到简单的答案。

2 个答案:

答案 0 :(得分:2)

实现此目的的一种方法是使walk()成为最终,并让它调用抽象方法。所以,

abstract class Animal {
  public final void walk() {
    // do stuff
    moreWalking();
  }

  protected abstract void moreWalking();
}
class Person extends Animal {
  protected void moreWalking() {
    // do stuff
  }
}

请注意,我使moreWalking使用受保护级别的访问者(非公共或私有),因此它在派生类中可以覆盖,但不能被客户端调用。

此外,这段代码是一个草图,我遗漏了很多重要的东西,比如Davidxx在回答中谈到的Animal界面。

一旦结果是Animal的子类必须'实施moreWalking()。这可以通过在基类中使用默认实现,或者通过不使用抽象方法并提供“空”来减轻这种情况。实现,像这样:

abstract class Animal {
    public final void walk() {
        // do stuff
        moreWalking();
    }

    protected void moreWalking() {

    }
}
class Person extends Animal {
    protected void moreWalking() {
        // do stuff
    }
}
class Dog extends Animal {
    // No need to implement moreWalking
}

这意味着您可以随时拨打walk()任何动物,无论是人还是狗,但这个人会有一些狗没有的额外东西。我保留了Animal摘要,因为你不希望别人创建它的实例。如果不是这种情况,那么你也可以删除它。

请注意,我们实际上并不经常在java中使用这种子类。通常我们使用接口和实现,子类共享公共实现(有时)。这种类型的继承很难理解和建模,事实证明它并没有那么有用。我们通常归结为一个名为Liskov的替换原则的规则,这意味着子类化仅在我们计划使用多态时才有用 - 这基本上归结为具有填充实例的超类型的集合我们希望迭代并调用相同方法的许多子类型(这是对它的粗略描述)。

答案 1 :(得分:-1)

两种方式都很好。你的第二个版本(super.walk())是大多数人喜欢的版本,包括我自己。也就是说,假设您没有其他特殊原因。

在要保留父行为的情况下,子类在将f作为super.f调用时实际上是有意义的。这里有两个注意事项:1)为了让你放松心情,不要把它想象成"把它留给孩子"就像孩子说的那样,我真的不知道,所以我会顺从我的父母,"并且你最终在所有子类中进行了super.f调用 - 可能看起来很轻微,烦人,额外的维护要求,但它确实有效。 2)你实际上应该让孩子的灵活性决定它是否会顺从父母,因为你很可能会发现有一天你创造了一个实际上没有孩子的奇怪案例想要使用父版本。

通过black magic完全隐藏某个类的某些功能,我已经失去了有人认为他们聪明的次数,并且没有办法打破他们设置的范例。这只会引起头痛。有一天,有人会想要为你的Platypus基类创建一个Animal子类,他们的子类将需要做一些奇怪的事情,因为它不符合规则为它的动物家庭。那个人(可能是你,将来踢你自己)可能想要在没有调用walk()的情况下致电super.walk()

另一个重要提示:有时您需要使用第一个示例中使用的格式:walk()childWalk()。一个这样的常见例子是父母的行为需要在孩子的行为之前和之后延伸。然后你可能需要这样的东西:

class Animal {
    void walk() {
        stuff();
        childWalk();
        moreStuff();
    }
}

通常你不应该需要这种情况。如果遇到这种情况,请花一点时间考虑问自己是否确实需要它,或者是否可以避免这种要求,但有时候是必要的。我有时遇到这个问题,需要在处理模拟时使用这种模式,例如,需要保留某些元素和因果场景的操作顺序。