Могу ли я иметь класс абстрактного 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();
}
}
Ответы
Ответ 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();
}