我想generateId()
在抽象超类的构造函数中调用抽象方法,其中此抽象方法依赖于相应子类的某些字段.为清楚起见,请考虑以下代码:
抽象类: SuperClass
public abstract class SuperClass { protected String id; public SuperClass() { generateId(); } protected abstract void generateId(); }
子类: Sub1
public class Sub1 extends SuperClass { private SomeType fieldSub1; public Sub1(SomeType fieldSub1) { this.fieldSub1 = fieldSub1; super(); } protected void generateId() { // Some operations that use fieldSub1 } }
子类: Sub2
public class Sub2 extends SuperClass { private SomeOtherType fieldSub2; public Sub2(SomeOtherType fieldSub2) { this.fieldSub2 = fieldSub2; super(); } protected void generateId() { // Some operations that use fieldSub2 } }
但是,子类构造函数不起作用,因为它super();
必须是构造函数中的第一个语句.
OTOH,如果我super();
在子类的构造函数的第一条语句,那么我就不能打电话generateId()
的SuperClass
.因为generateId()
在子类中使用字段,这些字段必须在使用之前初始化.
在我看来,"解决"这个问题的唯一方法是:删除generateId()
超类中的调用.generateId()
在每个子类的构造函数的末尾调用.但这会导致代码重复.
那么有没有办法解决这个问题而不重复我的代码?(也就是说,不调用每个子类generateId()
的构造函数的末尾?)
正如@GuillaumeDarmont指出的那样,在构造中使用可覆盖的方法是不好的做法.
您希望强制超类id
由子类初始化,因此更改构造函数:
public abstract class SuperClass { protected String id; public SuperClass(String id) { this.id = id; } }
此外,您可能希望更改generateId()
为静态方法,因为this
在调用超类构造函数之前无法引用:
public class Sub1 extends SuperClass { private SomeType fieldSub1; public Sub1(SomeType fieldSub1) { super(generateId(fieldSub1)); this.fieldSub1 = fieldSub1; } private static String generateId(SomeType fieldSub1) { // Some operations that use fieldSub1 } }
编辑:由于SuperClass
不知道如何计算id
,但你想强制它有一个id,你的一个选项是上面的解决方案.另一种选择是:
public abstract class SuperClass { private String id; public String getId() { if (id == null) { id = generateId(); } return id; } protected abstract String generateId(); } public class Sub1 extends SuperClass { private SomeType fieldSub1; public Sub1(SomeType fieldSub1) { this.fieldSub1 = fieldSub1; } @Override protected String generateId() { // Some operations that use fieldSub1 } }
两个解决方案之间的区别在于何时将计算id:在对象初始化时,或第一次请求id.这就是@Turing85正在讨论的内容.