我想在抽象超类的构造函数中调用抽象方法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()
?)
答案 0 :(得分:6)
正如@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正在讨论的内容。
答案 1 :(得分:0)
您可以尝试使用实例或静态初始化块。
初始化顺序:
例如:
class InitBlocksDemo {
private String name ;
InitBlocksDemo(int x) {
System.out.println("In 1 argument constructor, name = " + this.name);
}
InitBlocksDemo() {
name = "prasad";
System.out.println("In no argument constructor, name = " + this.name);
}
/* First static initialization block */
static {
System.out.println("In first static init block ");
}
/* First instance initialization block */
{
System.out.println("In first instance init block, name = " + this.name);
}
/* Second instance initialization block */
{
System.out.println("In second instance init block, name = " + this.name);
}
/* Second static initialization block */
static {
System.out.println("In second static int block ");
}
public static void main(String args[]) {
new InitBlocksDemo();
new InitBlocksDemo();
new InitBlocksDemo(7);
}
}
输出:
In first static init block
In second static int block
In first instance init block, name = null
In second instance init block, name = null
In no argument constructor, name = prasad
In first instance init block, name = null
In second instance init block, name = null
In no argument constructor, name = prasad
In first instance init block, name = null
In second instance init block, name = null
In 1 argument constructor, name = null
示例来源:link。
答案 2 :(得分:0)
您可以使 SuperClass
具有ParentType
变量,然后将类型转换为子类。对于我在评论中所说的内容,对术语有点偏僻感到抱歉。您不能在子类中更改超类的变量类型。但你可以施放。
我刚刚意识到你不能传递一个超类并将其转换为子类。你只能采用另一种方式,"all dogs are animals but not all animals are dogs"。
无论如何,我想到了另一种可能的解决方案。首先是变量:
public class ParentType { //stuff }
public class SomeType extends ParentType { //more stuff }
public class SomeOtherType extends ParentType { //even more stuff }
您可以使用this post中的想法。顺便说一下,我有点冒险进入未知世界,我之前没有这样做过。如果有人发现错误,请发表评论或编辑。
让你的班级通用:
public abstract class SuperClass<Type extends ParentType> {
protected Type field;
}
public class Sub1<SomeType> {
//now field is of type SomeType
}
public class Sub2<SomeOtherType> {
//now field is of type SomeOtherType
}