Могу ли я иметь абстрактный класс построителя в Java с цепочкой методов без выполнения небезопасных операций? - PullRequest
25 голосов
/ 28 апреля 2011

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

Пример кода, как я пытаюсь (и как это работает) ниже.Я хотел бы избежать приведения к 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();
   }
}

Ответы [ 3 ]

51 голосов
/ 28 апреля 2011

Вы хотите объявить 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, а не в основном детали реализации базового класса), если реализация будет более многословный.

8 голосов
/ 28 апреля 2011

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

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();
   }
}
0 голосов
/ 28 апреля 2011

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

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

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