ppasler
ppasler

Reputation: 3719

Implement clone defined in an Interface returning the concrete class?

I am reading DesignPatterns ('GoF patterns') and implementing the Example MazeGame. While reading about Prototypes, I ran into a thing writing the clone method (cloneIt()).

First of all my structure:

Interface: MapSite

AbstractClass: AbstractMapSite

Concrete Impl: Room, Door, Wall

My plan is to define cloneIt in MapSite and give a default implementation (simply calling Object.clone()), which can be overidden in the concrete classes. Now the cloneIt() method always returns the interface type MapSite.

Is there a way to 'force' Java to return the concrete class type (Room, Door, Wall) eventhough the method from the abstract class is used? This may avoid castings.

I know about the pros of Interfaces and why they are used :)

Solution

As @philipp-wendler and @davidxxx suggested, I ended up using generics. In case the shallow copy of Object.clone() is a problem, I override cloneIt() in the concrete class.

public interface MapSite<T> extends Serializable {
    T cloneIt();
}
...

public abstract class AbstractMapSite<T> implements MapSite<T> {
    public T cloneIt() {
        try {
            return (T) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}
...

public class Wall extends AbstractMapSite<Wall> {
    public Wall cloneIt(){
        // special code here
    }
}

Upvotes: 1

Views: 804

Answers (2)

Philipp Wendler
Philipp Wendler

Reputation: 11433

You need a recursive generic type:

public interface MapSite<T extends MapSite<T>> { 

  T cloneIt();
}

public abstract class AbstractMapSite<T extends AbstractMapSite<T>> implements MapSite<T> {
}

public class Room extends AbstractMapSite<Room> {
  public Room cloneIt() { ... }
}

This type forces non-abstract subclasses of MapSite to declare T as their own type, thus guaranteeing the correct return type of cloneIt.

You could also implement cloneIt in the abstract class if you call clone() and use an unchecked cast to T.

Upvotes: 1

davidxxx
davidxxx

Reputation: 131376

My plan is to define cloneIt in MapSite and give a default implementation (simply calling Object.clone()), which can be overidden in the concrete classes.

Be aware : the default implementation of Object.clone() makes a shallow copy of the cloned object and you should also implement the Cloneable interface if you don't want to see a CloneNotSupportedException be thrown.

So you should override both clone() and cloneIt(). It is confusing because a single method is enough. Just keep cloneIt() and forget the clumsy clone() method.

Is there a way to 'force' Java to return the concrete class type (Room, Door, Wall) eventhough the method from the abstract class is used? This may avoid castings.

With Generics yes you can do it.

For example :

public interface MapSite<T>{
  T cloneIt();
}

public abstract class AbstractMapSite<T> implements MapSite<T>{
  ...
}


public class Wall extends AbstractMapSite<Wall>{
   public Wall cloneIt(){
       ...
   }
}

Upvotes: 1

Related Questions