我想在一个抽象父类(super class)的构造函数中调用一个抽象方法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()使用子类中的字段,所以在使用之前必须在其中初始化这些字段。

对我来说,“解决”此问题的唯一方法是:在父类(super class)中删除对generateId()的调用。在每个子类的构造函数的末尾放置对generateId()的调用。但这会导致代码重复。

那么有什么方法可以解决这个问题而不重复我的代码吗? (就是说,没有在每个子类的构造函数的末尾调用generateId()吗?)

最佳答案

正如@GuillaumeDarmont指出的那样,在构造中使用可重写的方法是不好的做法。

您想让父类(super class)id由子类初始化,因此更改构造函数:

public abstract class SuperClass {
    protected String id;

    public SuperClass(String id) {
        this.id = id;
    }
}

另外,您可能希望将generateId()更改为静态方法,因为在调用父类(super class)构造函数之前无法引用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在讨论的内容。

09-06 13:33
查看更多