How to write subclass constructors without code duplication?

I want to call the abstract generateId() method in the constructor of the abstract superclass, where this abstract method depends on some fields of the corresponding subclasses. For clarity, consider the following code fragments:

Abstract class: SuperClass

 public abstract class SuperClass { protected String id; public SuperClass() { generateId(); } protected abstract void generateId(); } 

Subclass: 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 } } 

Subclass: 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 } } 

However, subclass constructors will not work because super(); must be the first statement in the constructor.

OTOH if I do super(); the first statement in subclass constructors, then I will not call generateId() in SuperClass . Because generateId() uses fields in subclasses where these fields must be initialized before use.

The only way I can "solve" this problem seems to me: delete the generateId() call in the superclass. Place a call to generateId() at the end of the constructors of each subclass. But this leads to code duplication.

So, is there a way to solve this problem without duplicating my code? (That is, without calling generateId() at the end of the constructors of each subclass?)

+6
source share
3 answers

As @GuillaumeDarmont noted, using redefined methods in constructs is bad practice.

You want the superclass id initialized with subclasses, so change the constructor:

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

Alternatively, you can change generateId() as a static method, since you cannot reference this before calling the superclass constructor:

 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 } } 

EDIT: Since SuperClass does not know how to calculate id , but you want it to force it, there is one of your options - this is the solution above. Another variant:

 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 } } 

The difference between the two solutions is about when the id will be calculated: when the object is initialized or when the identifier is first requested. Here is what @ Turing85 discussed.

+6
source

You can try using instance blocks or static initializations.

Initialization Order:

  • All static elements are in the order of their source code.
  • All member variables.
  • The constructor is called.

For instance:

 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);   } } 

Output:

 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 

Source of examples: link .

0
source

You can make the ParentType variable SuperClass and then apply the type in subclasses. Sorry for what I said in the comments, the terminology was a bit off. You cannot change the type of superclass variables in a subclass. But you can use.

I just realized that you cannot pass a superclass and apply it to a subclass. You can only go differently, because "all dogs are animals, but not all animals are dogs . "

In any case, I thought of another possible solution. Variables first:

 public class ParentType { //stuff } public class SomeType extends ParentType { //more stuff } public class SomeOtherType extends ParentType { //even more stuff } 

You can use the idea from this post . By the way, I kind of venture into the unknown, I have not done this before. If someone noticed a mistake with this, please comment or edit.

Make your class common:

 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 } 
0
source

All Articles