当我扩展某些课程时,例如:
public class First {
int id;
public First(int _id) {
id = _id;
}
}
和
public class Second extends First {
}
当我只需要做同样的事情时,我必须重新声明第二类的构造函数。 我可以:
public Second(int _id) {
super(_id);
}
但每次我更改父构造函数时,都需要编辑所有扩展类的构造函数。
如何完全扩展构造函数?
该死的,不要告诉我手机和天线,我多次使用过OOP。
现在我只问 - 我不能在扩展类上编写公共Class(){}并使用父构造函数吗?
好像我不能。好。
答案 0 :(得分:9)
定义子类时,它不会从超类继承构造函数。对于除默认超类构造函数之外的任何内容,您需要在每个子类构造函数中显式调用超类构造函数。 (这意味着,除其他外,如果基类没有默认构造函数,也不能有任何子类每个子类构造函数都必须显式调用超类构造函数 - 并且没有子类可以简单地编译器生成的默认构造函数。这有一个例外;请参阅下面的 [*] 。)
继承构造函数可能会导致各种问题。想象一下这样的事情:
class Base {
private int mId;
public Base(int id) {
mId = id;
}
. . .
}
现在我们想要派生一个具有第二个属性的类 - 一个标记 - 我们要确保它永远不会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);
如果您没有指定自己的任何构造函数,那么这是编译器生成非默认构造函数的唯一情况。实际上,语言语法甚至不允许您为匿名类声明构造函数;你必须依赖编译器为你生成一个。
答案 1 :(得分: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?