如何编写子类构造函数而不重复代码?

时间:2016-01-05 13:09:06

标签: java oop inheritance

我想在抽象超类的构造函数中调用抽象方法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()?)

3 个答案:

答案 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)

您可以尝试使用实例或静态初始化块。

初始化顺序:

  1. 源代码顺序中的所有静态项目。
  2. 所有成员变量。
  3. 调用构造函数。
  4. 例如:

    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
}