类扩展Abstract类实现Interface

时间:2016-05-30 09:27:54

标签: java interface abstract-class

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

这是我的例子: 案例是模拟一个包含树木信息的类。

首先,我会创建一个界面:

public interface Tree{
     public void grow();
}

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

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

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

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

  • Oak
  • 枫树
  • 云杉

所以这些树" tpyes"可以有个别变量。

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

这种方法在面向对象的设计原则中听起来是否正确,还是我完全错了呢?

3 个答案:

答案 0 :(得分:7)

这确实有效,但它没有多大意义,因为在这种情况下界面完全过时了。
您应该将grow方法添加到AbstractTree,如下所示:

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

    public abstract void grow();
}

使用界面是有意义的,如果你想拥有不同种类的植物,例如都应该能够生长。

interface Plant{
    void grow();
}

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

abstract class Flower implements Plant{
    void grow(){ /* do sth totally different */
}

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

答案 1 :(得分: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() {

    }   
}

答案 2 :(得分:5)

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

Tree界面 NOT 已过时。如果要为Tree建模,那么应该有一个Tree界面,明确说明每个Tree都有的方法。

特别是,我建议不要将其替换为AbstractTree类。有些人说你几乎不应该使用抽象类 (例如Jaroslav Tulach in "Practical API Design")。我至少会说你应该保守地使用非常。最重要的是:您应该尽量避免在其他类的公共接口中出现抽象类。例如,如果您有另一个类/接口,使用类似

的方法
void makeGrow(Tree tree) {
    System.out.println("Growing "+tree);
    tree.grow();
}

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

但即使如果你使用基于抽象的类,我建议保守地使用protected字段。或者,更一般地说,要注意从类继承的含义,如“第17项 - 继承的设计和文档,或者禁止它”"Effective Java" by Joshua Bloch中所述。

在许多情况下,您不希望继承类具有对字段的完全访问权限。因此,您至少应该考虑创建字段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; }
}