在Java中,据我所知,子类 不 继承具有参数的构造函数。
E.g。
public class Parent {
public Parent(int x) {
DoSomethingWithX(x);
}
}
public class Child extends Parent {
// Compile fails with "Implicit super constructor Parent() is undefined
// for default constructor. Must define an explicit constructor
}
修复它的唯一方法是在Child类中创建一个无用的传递构造函数:
public class Child extends Parent {
public Child(int x) {
super(x);
}
}
如果我有一个复杂的子类层次结构,有6-10个子类,那么在每个子类中添加带有参数的无意义的传递构造函数似乎是个坏主意!
有没有办法为大型类层次结构避免此问题?
我知道一个解决方案(有一个setter,必须与构造函数分开调用)才能获得参数。但是这个解决方案有几个很大的缺点,因为它们是不可接受的。
答案 0 :(得分:4)
像15年前的事情我有一个类似的问题,你的层次结构非常广泛(但只是稍微深一点)(是的,伙计们,有一个原因它是这样的) 。但由于它们都是从我们定期需要添加更多信息的基础派生而来的,很快就很明显向基础构造函数添加参数非常痛苦。
默认情况下不继承构造函数是件好事,但有时你希望继承它们。我有时想要一个编译时注释我可以添加到一个类来告诉编译器"自动生成任何超级构造函数的构造函数我没有明确地实现" 。但是我们没有它,如果它成功的话,JSR需要花费数年才能完成......
没有那个神奇的注释,我们使用的解决方案就是@psabbate mentioned in his/her comment:
如果您的构造函数接收到Map,或者包含您可能需要的每个参数的CustomClass类,该怎么办?这样,如果您需要更改参数和参数,则只能更改您感兴趣的类。
...但是参数类的特定类型层次结构(不是Map
)。
为了完整性,举个例子:
// The standard parameters needed
class StandardParams {
private String thisArg;
public StandardParams(String thisArg) {
this.thisArg = thisArg;
}
public String getThisArg() {
return this.thisArg;
}
}
// The base class
class Base {
public Base(StandardParams args) {
System.out.println("Base: " + args.getThisArg());
}
}
// A standard subclass
class Sub1 extends Base {
public Sub1(StandardParams args) {
super(args);
System.out.println("Sub1 thisArg: " + args.getThisArg());
}
}
对我来说,它甚至不是最差的。"具有表示层次结构所需的基本信息的类型具有表现力。
如果子类需要的信息多于标准参数(为我们提供),您可以选择第二个参数类类型作为第二个参数(但是树中有两种类型的构造函数) )或在参数类层次结构中使用继承;我们使用后者:
// Extended parameters (naturally you make these names meaningful)
class ExtendedParams extends StandardParams {
private String thatArg;
public ExtendedParams(String thisArg, String thatArg) {
super(thisArg);
this.thatArg = thatArg;
}
public String getThatArg() {
return this.thatArg;
}
}
// A subclass requiring extended parameter information
class Sub2 extends Base {
public Sub2(ExtendedParams args) {
super(args);
System.out.println("Sub2 thisArg: " + args.getThisArg());
System.out.println("Sub2 thatArg: " + args.getThatArg());
}
}
在我的案例IIRC中,我们只有三个参数类(树中特定分支的标准一个和两个子),层次结构中有30个主要类。
最后一点:对于我们来说,在构造它时,我们需要为参数类提供一组核心的东西,这些东西没有变化,并且有八个选项具有合理的基本默认值,但是你可以覆盖。为了避免参数类中构造函数的爆炸,我们最终在它们上做了一个穷人的构建器模式("穷人"因为我们自己创建了类建设者而不是把它分开;只是 严格要求它,改变的东西有廉价的默认值,因此实例总是处于有效状态,即使在构建时也是如此)。所以我们的建筑看起来像这样:
Thingy t = new Thingy(
new ThingyParams(basic, construction, info)
.withAnswer(42)
.withQuestion("Life, the Universe, and Everything")
);
答案 1 :(得分:2)
使用工厂方法和反射:
public class A {
protected A() {
// No longer do initialization in constructor
// initialize in init(), which is guaranteed to be called
}
protected A init(int i) {
// do all initialization with i in here
return this;
};
public static <T extends A> T create(Class<T> clazz, int i) {
try {
return (T) clazz.newInstance().init(i);
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException("not going to happen", e);
}
}
}
然后创建实例:
B b = A.create(B.class, 86);
根据您想要的学科水平,子课程可以如下:
public class B extends A {
// nothing special needed
}
但允许直接实例化B b = new B()
不需要int
,或者你可以收紧所有内容以防止这样:
public class B extends A {
protected B() {
super();
}
// nothing else special needed
}
需要所有实例化都通过工厂方法。
使用IDE可以轻松地重构在调用点更改代码。
如果基类更改为需要更多/不同的初始化变量,则只需更改工厂方法(子类中没有任何更改),或者可以添加新的工厂方法,保留旧方法以实现向后兼容。
答案 2 :(得分:0)
事实上,Parent
上只有一个带有参数的构造函数告诉我,如果没有所说的int,就不能创建Parent
。通过创建Child
,您可以通过继承创建Parent
。因为我们需要一个int来创建Parent
,所以Child
必须从某个地方提供它,无论是来自它自己的构造函数参数还是其他一些静态值。
如果您不想在Parent
的每个子类中提供传递构造函数,则需要在其上提供无参数构造函数并定义其含义。这可以是您已经拥有或替换它的构造函数的补充,这取决于您。但是,无法使用构造函数来执行您想要执行的操作。