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

Java 子类特定的代码-我应该使用抽象基类方法还是接口?,java,methods,implementation,abstract,overriding,Java,Methods,Implementation,Abstract,Overriding,我有一个超级类,动物,有两个子类,猫和狗。狗和猫都需要有特定的说话和移动方法。以下是实现这一目标的两个备选方案: Animal将有两个抽象方法,move()和speak();Dog、Cat和Dog都会覆盖这两种方法,因此它们是特定于它们的 我可以有一个具有通用动物方法move()和speak()的接口,其中两个子类实现了这些方法,因此它们又是特定于它们的 这些方法中的一种比另一种更合适吗?我意识到如果我有一个动物列表,我会写: ArrayList Animal<allAnimals>

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

  • Animal将有两个抽象方法,move()和speak();Dog、Cat和Dog都会覆盖这两种方法,因此它们是特定于它们的
  • 我可以有一个具有通用动物方法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();
    
    ArrayList Animal=new ArrayList():
    添加(新狗());
    添加(新的Cat());
    //如果我重写这些方法,我可以简单地执行以下操作:
    对于(动物a:所有动物)a.移动();
    //如果我实现了这些方法,我就不需要这样做。我需要将动物塑造成它的特殊动物。e、 如果我知道那动物是狗。
    狗aDog=(狗)a;
    a、 move();
    

    因此,重写和实现可能有一定的优势和劣势,这取决于它们所使用的情况。还有谁能详细说明一下吗

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

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

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

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

    无论是在使用接口还是继承的情况下。

    一般来说,经验法则是使用一个接口,前提是Java类可以实现多个接口,但只扩展一个类

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

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

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

    无论是在使用接口还是继承的情况下。

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

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

    这只是一些一般性的建议,因为每个用例都是不同的。 为了避免扩展的限制,可以使用其他技术,例如composition:()

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

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

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

    这只是一些一般性的建议,因为每个用例都是不同的。 为了避免扩展的限制,可以使用其他技术,例如composition:()

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

    首先,从子类的角度来看,重写或实现方法之间没有区别。所以,在所有情况下,您编写的代码都将完全相同(在java中,所有方法都像C++ ++代码>虚拟<代码>函数;调用的方法是引用的实际类之一)。 因此,决定是定义一个超类,它是一个接口、一个抽象类或一个可实例化类

    如果有必要创建超类的对象(
    newanimal()
    ),则应使用instantiable类

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

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


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

    首先,从子类的角度来看,重写或实现一个方法是没有区别的。所以,在所有情况下,您编写的代码都将完全相同(在java中,所有方法都像C++ ++代码>虚拟<代码>函数;调用的方法是引用的实际类之一)。 因此,决定是定义一个超类,它是一个接口、一个抽象类或一个可实例化类

    如果有必要创建超类的对象(
    newanimal()
    ),则应使用instantiable类

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

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

    这取决于项目的范围
    static public abstract class Animal implements MoverAndSpeaker {
        @Override
        public abstract void move();
        public abstract void speak();
    }