Подтвердить что ты не робот

Могу ли я иметь класс абстрактного builder в java с цепочкой методов, не выполняя небезопасные операции?

Я пытаюсь иметь абстрактный базовый класс для некоторых классов-конструкторов, поэтому я могу легко повторно использовать код между реализациями Builder. Я хочу, чтобы мои разработчики поддерживали цепочку методов, поэтому метод должен возвращать экземпляр "this" самого конкретного типа. Я подумал, что я, возможно, сделаю это с помощью дженериков. К сожалению, мне не удалось это сделать без использования небезопасных операций. Возможно ли это?

Пример кода, как я его пытаюсь (и как это работает) ниже. Я бы хотел, чтобы избежать нажатия на T в "foo()" (что вызывает непроверенное предупреждение), можно ли это сделать?

public class Builders
{
   public static void main( final String[] args )
   {
      new TheBuilder().foo().bar().build();
   }
}


abstract class AbstractBuilder<T extends AbstractBuilder<?>>
{
   public T foo()
   {
      // set some property
      return (T) this;
   }
}


class TheBuilder extends AbstractBuilder<TheBuilder>
{
   public TheBuilder bar()
   {
      // set some other property
      return this;
   }

   public Object build()
   {
      return new Object();
   }
}
4b9b3361

Ответ 1

Вы хотите объявить T как extends AbstractBuilder<T> в AbstractBuilder.

Используйте метод abstract protected для получения this типа T.

abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
    protected abstract T getThis();

    public T foo() {
        // set some property
        return getThis();
    }
}


class TheBuilder extends AbstractBuilder<TheBuilder> {
    @Override protected TheBuilder getThis() {
        return this;
    }
    ...
}

В качестве альтернативы отбросить параметр типового типа, полагаться на ковариантные типы возвращаемых данных и сделать код более чистым для клиентов (хотя обычно они будут использовать TheBuilder, а не в основном детали реализации базового класса), если сделать реализацию более многословным.

Ответ 2

Один из альтернатив - не использовать дженерики, но использовать переопределения:

abstract class AbstractBuilder
{
   public AbstractBuilder foo()
   {
      // set some property
      return this;
   }
}

class TheBuilder extends AbstractBuilder
{
   @Override public TheBuilder foo()
   {
      super.foo(); return this;
   }
   public TheBuilder bar()
   {
      // set some other property
      return this;
   }

   public Object build()
   {
      return new Object();
   }
}

Ответ 3

Это должно помочь:

abstract class AbstractBuilder<T extends AbstractBuilder<?>>
{
   public AbstractBuilder<T> foo()
   {
      // set some property
      return (AbstractBuilder<T>) this;
   }

   abstract AbstractBuilder<T> bar();
   abstract Object build();
}