特定于子类的代码 - 我应该使用抽象基类方法还是接口?

时间:2013-04-14 21:40:15

标签: java methods implementation abstract override

我有一个超类动物,有两个子类,猫和狗。狗和猫都需要有特定的说话和移动方法。以下是实现此目的的两种选择:

  1. Animal将有两个抽象方法,move()和speak();狗,猫和狗各自覆盖两种方法,因此它们是特定于它们的。
  2. 我可以拥有一个具有通用动物方法move()和speak()的接口,两个子类实现这些方法,因此它们又是特定于它们的。
  3. 这些方法中的一种比其他方法更合适吗?我知道如果我有动物的ArrayList,我会写:

    ArrayList Animal<allAnimals> = new ArrayList <Animal>():
    allAnimals.add(new Dog());
    allAnimals.add(new Cat());
    
    //If I override the methods, I can simply go: 
    for (Animal a: allAnimals) a.move();
    
    //Where as if I implemented the methods, I would not need be able to do this. I would need to cast the animal as its specific animal. e.g. If I knew the animal was a dog.
    
    Dog aDog= (Dog) a; 
    a.move();
    

    因此,压倒和实施可能会有一些优点和缺点,具体取决于他们使用的情况。其他人可以详细说明吗?

7 个答案:

答案 0 :(得分:3)

作为一般经验法则,当且仅当您的扩展类将共享大量行为时,才使用扩展(抽象类)。在这种情况下,可以使用扩展来消除重复代码的需要,这是A Good Thing(TM)。

另一方面,如果您的类只需要具有相同的签名,那么请使用更灵活的接口(例如,您可以实现多个接口,但只能从一个类扩展)

这只是一些通用的建议,因为每个用例都不同。 为了避免扩展的限制,可以使用其他技术,例如,组合:(http://en.wikipedia.org/wiki/Composition_over_inheritance)。

最后但并非最不重要的是,我认为你的代码中有些误。 您可以以完全相同的方式调用基类上的方法,无论它们是从抽象类还是接口继承它们。

答案 1 :(得分:3)

首先,从子类的角度来看,覆盖或实现方法之间没有区别。因此,您编写的代码在所有情况下都将完全相同(在Java中,所有方法都像C ++ virtual函数一样;调用的方法是被引用的实际类之一。)

因此,决定是定义一个超类,即接口,抽象类或可实例化的类。

如果有意义创建超类(new Animal())的对象,则应使用可实例化的类。

抽象类,当没有超类的对象但某些方法的实现将由所有(或几乎所有)子类共享时。

接口,当您只定义合同时,实现留给每个子类。

决定使用哪个问题取决于问题的范围。例如,如果move()仅表示“更改动物的x,y坐标”,则可能它应该是抽象类中的实现方法。但是如果你必须定义动物的移动方式,那么Animal会更好一个接口,所以每个子类都定义它。

答案 2 :(得分:1)

一般来说,经验法则是使用接口,如果可以导致Java类可以实现许多接口但只扩展一个类。

只有当几个类有很多共同之处且你希望能够重用相同的代码时,才应该使用扩展(继承)。

顺便说一句,在您提供的代码中,您可以使用:

for (Animal a: allAnimals) a.move();

在您使用接口和继承的情况下。

答案 3 :(得分:1)

在这种情况下,它可能取决于你是否想要一个默认的move()实现,所以Dog和Cat不必提供自己的实现。据推测,move()方法在猫狗之间具有很多共性,因此你可以通过超类中的常见实现获得更好的可重用性。

答案 4 :(得分:1)

//Where as if I implemented the methods, I would not need be able to do this. I would need to cast the animal as its specific animal. e.g. If I knew the animal was a dog.

Dog aDog= (Dog) a; 
a.move();

不,你根本不需要这样做。如果Animal是一个接口而不是一个抽象类,那么为抽象类显示的过程就可以正常工作。

答案 5 :(得分:1)

从概念上讲,抽象类描述了什么是东西,一个界面描述了它的作用。在这种情况下,将Animal作为抽象类是合适的,因为猫或狗是动物。在功能上,选择哪个选项可以产生最少的编码/最干净的类层次结构。

答案 6 :(得分:0)

首先,你有第三个选项,即:有一个界面(比如MoverAndSpeaker;虽然那是一个蹩脚的名字),并且有抽象基类动物&#39; implmenent&#39;它:

static public abstract class Animal implements MoverAndSpeaker {
    @Override
    public abstract void move();
    public abstract void speak();
}

为什么你甚至想要这样的东西?

与此处的一些答案不同,我认为语义一致性是最重要的考虑因素,避免代码重复等问题要么自己解决,要么重视少数。

如果我在你的位置,我将适用的关键标准是:

  

&#34;是否有移动和说话的物体,这些物体不是动物?&#34;

  • 如果答案是&#34; no&#34;,那么移动和说话就是动物所做的事情。因此,请使用抽象方法 only
  • 如果答案是&#34;是&#34;,那么我问自己
  

&#34;所有动物都会移动并说话吗?&#34;

  • 如果次要答案为&#34;是&#34;,则使用上述代码(假设您不希望所有动物都有默认实施)。
  • 如果次要答案为&#34; no&#34;,请仅考虑界面。虽然你可能想要Dog,Cat和Animal之间的中间课程 - 如果你在所有动物之间有一些共享的代码或字段,它们也可以移动和说话。

最后,如果所有动物没有任何共同点,那么正如其他答案中所建议的那样,您可能根本不需要动物基类。