Цепочка методов: как использовать трюк getThis () в случае многоуровневого наследования - PullRequest
6 голосов
/ 11 марта 2012

Мой вопрос в контексте Цепочка методов + наследование плохо сочетаются друг с другом? .Но, к сожалению, во всех примерах / ответах метода сцепления используется один уровень наследования.Мой сценарий использования включает многоуровневое наследование, например,

abstract class PetBuilder{...}
class  DogBuilder extends PetBuilder{..}
class DogType1Builder extends DogBuilder {...}

. Для создания объекта Dog я буду использовать либо DogBuilder, либо DogType1Builder

, как использовать метод getThis для описанного выше варианта использования?

Я хочу использовать шаблон построения для построения сложного объекта Dog (объектная модель Dog) ". DogType1 будет иметь некоторые дополнительные свойства.

, чтобы использовать объявление getThis Trick длявышеупомянутые классы станут похожи на

abstract class PetBuilder<T extends PetBuilder<T>>
class DogBuilder<T extends DogBuilder<T>> extends PetBuilder<DogBuilder<T>>
class DogType1Builder extends DogBuilder<DogType1Builder>

Теперь это создает две проблемы

1.builder метод в «DogBuilder» будет выглядеть так:

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.Как DogBuilder имеетстать параметризованным, поэтому для создания экземпляра "DogBuilder" мне придется использовать

DogBuilder<DogBuilder> builder=new DogBuilder(); //passing <DogBuilder> type ...real pain

Есть ли лучший способ?

Ответы [ 2 ]

8 голосов
/ 11 марта 2012

Корень вашей проблемы - проблема разработки класса: вы пытаетесь унаследовать от конкретного класса , что почти всегда является ошибкой, и (ваш конкретный пример) неизбежно приведет к многочисленным проблемам , Чтобы придерживаться примера, приведенного в упомянутой теме, вам не следует создавать экземпляр Dog, поскольку в такой вселенной вообще не может быть Dog с, более чем Pet с - только Poodle с, NewFoundland s, Spaniel s и т. Д. Следовательно, getThis не следует реализовывать в классах среднего уровня (абстрактных), только в (конкретных) листовых классах. И во всех абстрактных классах среднего уровня следует ссылаться только на параметр общего типа T, а не на фактическое имя класса.

Вот пример в ответе на упомянутую ветку , переписанный в соответствии с вышеуказанными правилами:

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();
  }
}
1 голос
/ 11 марта 2012

Я не верю, что вы можете использовать трюк getThis для нескольких уровней наследования. У вас есть суперкласс Pet<T extends Pet<T>>, первый подкласс Dog extends Pet<Dog> и второй подкласс Poodle extends Dog. С трюком getThis у вас есть метод protected T getThis() и такие методы, как public T rollOver(). Это означает, что как Poodle, так и Dog имеют методы protected Dog getThis() и public Dog rollOver().

.

Я бы следовал предложению Майкла Майерса , чтобы использовать ковариантные типы возврата.

...