Bick
Bick

Reputation: 18521

require a java inheritance design tip

I have an abstract super class and a few derived class.
I want to create a method

createNew()   

when invoked on a derived class it will create a new instance of the derived with a smart copy of all of its members.
All the members that are important for the creation are in the abstract class so the code should be the same.
can I write the implementation of createNew() in the super class?

something like that

SonA sonA2 = sonA1.createNew()   
SonB sonB2 = sonB1.createNew()  

the abs superclass should do the implementation because the code is identical .
thanks.

Upvotes: 1

Views: 137

Answers (4)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

The implementation should be split between the abstract and the concrete classes. You can do it using the Template Method pattern:

public abstract class AbstractSon {
    protected abstract AbstractSon createNewImpl();
    public AbstractSon createNew() {
        AbstractSon res = createNewImpl();
        res.name = "default name";
        return res;
    }
}
public class Son1 extends AbstractSon {
    protected AbstractSon createNewImpl() {
        return new Son1();
    }
}
public class Son2 extends AbstractSon {
    protected AbstractSon createNewImpl() {
        return new Son2();
    }
}

You can split the responsibility differently to get the precise return type:

public abstract class AbstractSon {
    protected void prepare(AbstractSon toPrepare) {
        toPrepare.name = "default name";
    }
}
public class Son1 extends AbstractSon {
    public Son1 createNew() {
        Son1 res = new Son1();
        prepare(res);
        return res;
    }
}
public class Son2 extends AbstractSon {
    public Son2 createNew() {
        Son2 res = new Son2();
        prepare(res);
        return res;
    }
}

Upvotes: 3

NPE
NPE

Reputation: 500357

One possibility is to factor the copying out into a separate method, and call that from the derived classes' createNew():

abstract class Base {

  public abstract Base createNew();

  protected void populate(Base out) {
    // copy the data from `this' to `out'
  }
}

class Derived1 extends Base {
  public Derived1 createNew() {
    Derived1 ret = new new Derived1();
    populate(ret);
    return ret;
  }
}

class Derived1 extends Base {
  public Derived2 createNew() {
    Derived2 ret = new new Derived2();
    populate(ret);
    return ret;
  }
}

Upvotes: 1

Aaron Digulla
Aaron Digulla

Reputation: 328594

You can write this code (using reflection: getClass().newInstance() to get an instance of the actual class instead of the class in which the method is defined) but it has some problems. For example what do you intend to return from this method?

You have to return the super type which means you have to cast for every invocation.

The usual solution is to define a copy() method that you define on every level. Then you can do this:

 class SonA {
     SonA createNew() {
         SonA result = new SonA();
         this.copy( result );
         return result;
     }

     void copy( SonA target ) {
         super.copy( target );

         // copy my fields to target ...
     }
 }

You may also want to read about copy constructors.

Upvotes: 1

Marko Topolnik
Marko Topolnik

Reputation: 200158

Make an abstract method that will serve as a callback used by your createNew method -- and implement that method in each subclass. createNew can be a concrete, even final method of the common superclass. The mentioned abstract method will return a new instance of the respective subclass.

An alternative approach is to find out through reflection on which class createNew was called and make a new instance based on that.

Upvotes: 1

Related Questions