【问题标题】:Subclass-specific code - should I use abstract base class methods or an interface?特定于子类的代码 - 我应该使用抽象基类方法还是接口?
【发布时间】:2013-04-14 21:40:15
【问题描述】:

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

  1. Animal 会有两个抽象方法,move() 和 speak(); Dog、Cat 和 Dog 分别覆盖这两个方法,因此它们是特定于它们的。
  2. 我可以有一个具有通用动物方法 move() 和 speak() 的接口,两个子类实现这些方法,因此它们再次特定于它们。

其中一种方法是否比另一种更合适?我意识到如果我有一个动物数组列表,我会写:

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

因此,根据使用情况,覆盖和实施可能具有某些优点和缺点。其他人可以详细说明吗?

【问题讨论】:

    标签: java methods implementation abstract overriding


    【解决方案1】:

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

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

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

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

    接口,当你只定义契约而实现留给每个子类时。

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

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

      • 谢谢 - 很棒的信息 - 如果可以的话 + 代表你。
      • 您可以接受,对我或任何您认为最能回答您的问题的人。 :)
      【解决方案3】:

      在这种情况下,它可能取决于您是否希望拥有 move() 的默认实现,因此 Dog 和 Cat 不必提供自己的实现。大概 move() 方法在猫和狗之间有很多共同点,因此您可以通过超类中的通用实现具有更好的可重用性。

      【讨论】:

        【解决方案4】:
        //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 是一个接口而不是一个抽象类,那么您为抽象类显示的过程就可以正常工作。

        【讨论】:

        • 我想把Animal作为一个抽象类,有通用的方法和属性,然后实现move和speak。不知道我会怎么称呼它。如果我这样做,我需要将对象作为它的特定动物..
        • 如果您只调用抽象类或接口中定义的方法,则不会。无论是抽象类还是定义这些方法的接口,都没有区别。
        • 好的,如果我有一个包含狗和猫的动物 (allAnimals) 的 ArrayList,那么对于狗和猫,我实现了包含 speak 方法的 Vocalizable。我无法使用 for (Animal a: allAnimals) a.speak(); 访问 speak 方法我需要将其转换为狗或猫,因为它们是知道不讲抽象类的类。无论如何,这就是我的理解 - 我可能会对你的意思感到困惑,如果我错了,请详细说明?
        • 您可以将其转换为可发声...现在我对您的难题有了更多了解,也许我会更新我的答案。
        • 不会费心更新答案,因为您接受的答案很好。
        【解决方案5】:

        从概念上讲,抽象类描述事物的本质,接口描述事物的作用。在这种情况下,将 Animal 作为抽象类是合适的,因为 Cat 或 Dog 是 Animal。从功能上讲,选择导致最少编码量/最干净的类层次结构的选项。

        【讨论】:

          【解决方案6】:

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

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

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

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

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

          【讨论】:

            【解决方案7】:

            首先,您有第三种选择,即:拥有一个接口(例如 MoverAndSpeaker;虽然这名字有点蹩脚),并拥有 抽象基类 Animal 'implmenent'它:

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

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

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

            如果我处于你的位置,我将应用的关键标准是:

            “有没有不是动物的会动会说话的物体?”

            • 如果答案是“否”,那么动物会做移动和说话。因此,只使用抽象方法
            • 如果答案是“是”,那我问自己

            “所有的动物都会移动和说话吗?”

            • 如果第二个答案是“是”,则使用上面的代码(假设您不希望所有动物都使用默认实现)。
            • 如果第二个答案是“否”,则只考虑接口。虽然您可能想要一个介于 Dog、Cat 和 Animal 之间的中间类 - 如果您在所有也会移动和说话的 Animals 之间有一些共享代码或字段。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-03-11
              • 2023-03-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-13
              • 1970-01-01
              相关资源
              最近更新 更多