5

当我扩展一些类时,例如:

public class First {
    int id;
    public First(int _id) {
        id = _id;
    }
}

public class Second extends First {

}

当我只需要做同样的事情时,我必须重新声明 Second class 的构造函数。我可以:

public Second(int _id) {
    super(_id);
}

但是每次我更改父构造函数时,都需要编辑所有扩展类的构造函数。

如何完全扩展构造函数?

该死的,不要告诉我电话和天线,我用过很多次OOP。

现在我只问 - 我不能在扩展类上编写 public Class(){} 并使用父构造函数吗?

好像我做不到。好的。

4

2 回答 2

10

定义子类时,它不会从超类继承构造函数。对于默认超类构造函数以外的任何内容,您都需要在每个子类构造函数中显式调用超类构造函数。(这意味着,除其他外,如果基类没有默认构造函数,则任何子类也不能每个子类构造函数都必须显式调用超类构造函数——并且任何子类都不能仅具有编译器生成的默认构造函数。有一个例外;请参阅下面的[*]。)

继承构造函数可能会导致各种问题。想象一下这样的事情:

class Base {
    private int mId;
    public Base(int id) {
        mId = id;
    }
    . . .
}

现在我们要派生一个具有第二个属性的类——一个标签——我们要确保它是 never null。所以我们写如下:

class Derived extends Base {
    private Object mTag;
    public Derived(Object tag, int id) {
        super(id);
        if (tag == null) {
            throw new IllegalArgumentException("tag cannot be null");
        }
        mTag = tag;
    }
    . . .
}

现在想象构造一个Derived这样的实例:

Derived derived = new Derived(3);

如果构造函数被继承,这可能是合法的。这意味着什么?一方面,mTag将设置为其默认值null. 没有办法Derived强制执行所有实例都Derived必须具有非空标记的规则。

以需要更多输入为代价,Java 精确地排除了继承构造函数,以允许每个类完全控制其实例的创建方式。

[*]在一种情况下,编译器会自动生成一个非默认构造函数。考虑Base上面的类和这段代码:

Base foo = new Base(3) {
    . . . // some extended functionality
};

现在foo正在初始化为Base. 为了方便参考,我们称这个子类Base$Anon。编译器将生成等效于:

class Base$Anon extends Base {
    Base$Anon(int id) {
        super(id);
    }
    . . . // some extended functionality
}

Base foo = new Base$Anon(3);

如果您不指定自己的任何构造函数,这是编译器生成非默认构造函数的唯一情况。事实上,语言语法甚至不允许您为匿名类声明构造函数。您必须依靠编译器为您生成一个。

于 2012-05-20T16:30:27.743 回答
2

这个限制是我更喜欢组合而不是继承的原因之一。考虑改用这样的代码:

public class Second {
    private final First first;

    public Second(final First first) {
         this.first=first;
    }

    public int getId() {
        return first.getId();
    }
}

在我看来,这种结构还使您的两个类的耦合度降低,并使类之间的关系更加清晰。有关组合优于继承的更全面讨论,请参见例如这个问题:Prefer composition over inheritance?

于 2012-05-20T16:24:56.533 回答