类扩展Abstract类实现Interface

use*_*409 9 java abstract-class interface

上周末我读了一些关于接口,抽象类和设计原则的东西.最后我有点困惑,我试图建立一个我学到的(或者我以为我学到的)的例子.

这是我的例子:案例是模拟一个包含有关树的信息的类.

首先,我会做一个界面:

public interface Tree{
     public void grow();
}
Run Code Online (Sandbox Code Playgroud)

该接口包含应由具体树实现的所有方法.到目前为止这么好,但是这样的树需要一些在所有树家族上共享的属性(变量).为此,我将使用一个抽象类:

public abstract class AbstractTree implements Tree {
    private String barColor;
    private int maxHeight;
    private boolean isEvergreen;
}
Run Code Online (Sandbox Code Playgroud)

这是正确的方式,还是我不能就其他类中的属性(变量)签订合同?

属性部分完成后,我希望有3种类型的树.

  • 橡木
  • 云杉

因此,这些树"tpyes"中的每一个都可以具有单独的变量.

public class OakTreeImpl extends AbstractTree{
    private String barColor;
    private int maxHeight;
    private boolean isEvergreen;
    private String foo;
    @Override
    public void grow() {
    }   
}
Run Code Online (Sandbox Code Playgroud)

这种方法在面向对象的设计原则中是否正确,或者我完全错了吗?

000*_*000 7

这确实有效,但它没有多大意义,因为在这种情况下接口完全过时了.
您应该将grow方法添加到您的AbstractTree喜欢:

public abstract class AbstractTree{
    protected String barColor;
    protected int maxHeight;
    protected boolean isEvergreen;

    public abstract void grow();
}
Run Code Online (Sandbox Code Playgroud)

如果你想拥有不同种类的植物,那么使用界面就有意义了.

interface Plant{
    void grow();
}

abstract class Tree implements Plant{
    void grow(){ /* do sth */ }
}

abstract class Flower implements Plant{
    void grow(){ /* do sth totally different */
}
Run Code Online (Sandbox Code Playgroud)

接口的目的是在具有不同实现的多个类中提供相同的方法,而抽象类提供在其所有子类中共享的方法和属性.
如果抽象类中的方法也是抽象的,则每个子类都必须自己实现它.


Jud*_*han 6

我宁愿将实例变量标记为protected.

因为子类可以访问超类的所有受保护成员.当且仅当,父类和子类在同一个包中

public abstract class AbstractTree implements Tree {
    protected String barColor;
    protected int maxHeight;
    protected boolean isEvergreen;
}

public class OakTreeImpl extends AbstractTree{

    // I can access barColor, maxHeight, isEvergreen in this class

    @Override
    public void grow() {

    }   
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*o13 5

尽管这可能是部分主观的,但我必须同意到目前为止给出的其他答案。

Tree接口不是过时的。当您要为建模时Tree,应该有一个Tree接口,明确说明每个接口Tree都有的方法。

特别是,我建议不要将其简单地替换为AbstractTree类。有人说,你应该很难使用抽象类在所有(例如雅罗斯拉夫Tulach在“实用API设计”)。我至少要说您应该非常保守地使用它们。最重要的是:您应尽量避免让抽象类出现在其他类的公共接口中。例如,如果您有另一个类/接口,则使用类似

void makeGrow(Tree tree) {
    System.out.println("Growing "+tree);
    tree.grow();
}
Run Code Online (Sandbox Code Playgroud)

然后更换的这个外观TreeAbstractTree将降低灵活性。您将永远无法使用继承自一个类的类AbstractTree-考虑到您只能继承一个类,这可能是一个严重的限制。(您始终可以实现多个接口-因此,这里的接口并不限制灵活性)。

但是,即使如果您使用抽象类的基础,我建议使用protected保守的领域。或者,更一般而言,请注意从类继承的含义,如Joshua Bloch“ Effective Java”的“第17项-继承的设计和文档”中所述。

在许多情况下,您不希望继承的类具有对字段的完全访问权限。因此,您至少应该考虑创建字段private,并且仅提供protected要授予继承类访问权限的方法。

public interface Tree{
    public void grow();
}

abstract class AbstractTree implements Tree {

    // Do the values of these fields ever change? If not,
    // then make them final, and set them only in the
    // constructor
    private final String barColor;
    private final int maxHeight;
    private final boolean evergreen;

    protected AbstractTree(...) { ... }

    // Subclasses are only allowed to read (but not write) these fields
    protected final String getBarColor() { return barColor; }
    protected final intgetMaxHeight() { return maxHeight; }
    protected final boolean isEvergreen() { return evergreen; }
}
Run Code Online (Sandbox Code Playgroud)