My question is in context of Method chaining + inheritance don’t play well together?.
But unfortunately all examples/answers of method chaining uses single level of inheritance.
My usecase involves multi level of inheritance for e.g
abstract class PetBuilder{...}
class DogBuilder extends PetBuilder{..}
class DogType1Builder extends DogBuilder {...}
To construct a Dog Object,i will be using either DogBuilder or DogType1Builder
how to use getThis trick for the above use case?
I want to use builder pattern for constructing a complicated Dog object(Dog Object Model)".
DogType1 will have some added properties.
so to use getThis Trick declaration of above classes will become like
abstract class PetBuilder<T extends PetBuilder<T>>
class DogBuilder<T extends DogBuilder<T>> extends PetBuilder<DogBuilder<T>>
class DogType1Builder extends DogBuilder<DogType1Builder>
Now this creates two problems
1.builder method in 'DogBuilder' will look like
public T someMethodInDog(String dogName) {
..
return (T)this; ///i dont want type casting and i cant use getThis Trick Here (compiler reports error for conversion from DogBuilder to T)
}
2.As DogBuilder has become parameterised,so to create instance of "DogBuilder" i will have to use
DogBuilder<DogBuilder> builder=new DogBuilder(); //passing <DogBuilder> type ...real pain
Is there a better way?
The root of your problem is a class design issue: you are trying to inherit from a concrete class, which is almost always a mistake, and (your case in point) is bound to cause numerous problems. To stick with the example given in the referred thread, you should not be instantiating Dog
, as in such a universe there can exist no Dog
s in general, any more than Pet
s - only Poodle
s, NewFoundland
s, Spaniel
s etc. Consequently, getThis
should not be implemented in mid-level (abstract) classes, only in the (concrete) leaf classes. And in all mid-level abstract classes, you should only refer to the generic type parameter T
, instead of the actual class name.
Here is the example in the answer to the referred thread rewritten according to the above rules:
public class TestClass {
static abstract class Pet <T extends Pet<T>> {
private String name;
protected abstract T getThis();
public T setName(String name) {
this.name = name;
return getThis(); }
}
static class Cat extends Pet<Cat> {
@Override protected Cat getThis() { return this; }
public Cat catchMice() {
System.out.println("I caught a mouse!");
return getThis();
}
}
// Dog is abstract - only concrete dog breeds can be instantiated
static abstract class Dog<T extends Dog<T>> extends Pet<T> {
// getThis is not implemented here - only in concrete subclasses
// Return the concrete dog breed, not Dog in general
public T catchFrisbee() {
System.out.println("I caught a frisbee!");
return getThis();
}
}
static class Poodle extends Dog<Poodle> {
@Override protected Poodle getThis() { return this; }
public Poodle sleep() {
System.out.println("I am sleeping!");
return getThis();
}
}
static class NewFoundland extends Dog<NewFoundland> {
@Override protected NewFoundland getThis() { return this; }
public NewFoundland swim() {
System.out.println("I am swimming!");
return getThis();
}
}
public static void main(String[] args) {
Cat c = new Cat();
c.setName("Morris").catchMice();
Poodle d = new Poodle();
d.setName("Snoopy").catchFrisbee().sleep();
NewFoundland f = new NewFoundland();
f.setName("Snoopy").swim().catchFrisbee();
}
}
I don't believe you can use the getThis
trick for multiple levels of inheritance. You have the super class, Pet<T extends Pet<T>>
, the first subclass, Dog extends Pet<Dog>
, and the second subclass Poodle extends Dog
. With the getThis
trick, you have the method protected T getThis()
and methods like public T rollOver()
. This means that both Poodle
and Dog
have the methods protected Dog getThis()
and public Dog rollOver()
.
I would follow Michael Myers' suggestion to use covariant return types.