Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method Chaining: How to use getThis() trick in case of multi level inheritance

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?

like image 859
harrybvp Avatar asked Mar 11 '12 13:03

harrybvp


2 Answers

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 Dogs in general, any more than Pets - only Poodles, NewFoundlands, Spaniels 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();
  }
}
like image 97
Péter Török Avatar answered Oct 17 '22 20:10

Péter Török


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.

like image 34
Jeffrey Avatar answered Oct 17 '22 20:10

Jeffrey