Java: возвращающий подкласс в подклассе метода суперкласса
Я работаю над проблемой, где есть несколько реализаций Foo
, сопровождаемых несколькими FooBuilder
. В то время как Foo
разделяет несколько общих переменных, которые необходимо установить, они также имеют разные переменные, для которых требуется соответствующее FooBuilder
реализовать некоторые конкретные функции. Для лаконичности я хотел бы, чтобы сеттеры FooBuilder
использовали цепочку методов, например:
public abstract class FooBuilder {
...
public FooBuilder setA(int A) {
this.A = A;
return this;
}
...
}
и
public class FooImplBuilder extends FooBuilder{
...
public FooImplBuilder setB(int B) {
this.B = B;
return this;
}
public FooImplBuilder setC(int C) {
this.C = C;
return this;
}
...
}
И так далее, с несколькими различными реализациями FooBuilder
. Это технически делает все, что я хочу, однако этот подход чувствителен к порядку вызова методов при выполнении цепочки методов. Ниже приведен метод undefined ошибки компиляции:
someFoo.setA(a).setB(b)...
Требование, чтобы разработчик думал о порядке вызовов методов в цепочке. Чтобы этого избежать, я хотел бы, чтобы сеттеры в FooBuilder
каким-то образом возвращали фактический исполняемый подкласс. Однако я не уверен, как это сделать. Каков наилучший подход?
Ответы
Ответ 1
Это хороший вопрос и реальная проблема.
Самый простой способ справиться с этим в Java, вероятно, предполагает использование дженериков, как указано в ответе Йохена.
Хорошее обсуждение проблемы и разумное решение в этой записи в блоге Использование Наследования с Свободными Интерфейсами, в котором сочетаются общие понятия с определением метода getThis()
переопределить в подклассах строителя, чтобы решить проблему всегда возврата строителя правильного класса.
Ответ 2
Здесь может быть полезен дженерик.
Если вы объявите setA() что-то вроде этого (псевдокод)
<T> T setA(int a)
компилятор должен иметь возможность определять реальные типы, и если это не так, вы можете дать подсказки в коде, например
obj.<RealFoo>setA(42)
Ответ 3
Найдя этот отличный ответ, теперь я расскажу о нем.
public class SuperClass<I extends SuperClass>
{
@SuppressWarnings( "unchecked" ) // If you're annoyed by Lint.
public I doStuff( Object withThings )
{
// Do stuff with things.
return (I)this ; // Will always cast to the subclass. Causes the Lint warning.
}
}
public class ImplementationOne
extends SuperClass<ImplementationOne>
{} // doStuff() will return an instance of ImplementationOne
public class ImplementationTwo
extends SuperClass<ImplementationTwo>
{} // doStuff() will return an instance of ImplementationTwo