Reputation: 3719
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
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
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