Учитывая иерархию классов, в которой базовый класс определяет рекурсивный тип:
abstract class A<T extends A<T>> { }
Как я могу объявить другой класс (который не должен быть универсальным в T, потому что такой T может изменяться в течение времени жизни объекта) с полем, которое может содержать любой подкласс A?
Не работает следующее:
public class B {
//fails to compile, because the capture of ? is not sufficiently narrow
private A<?> a;
public <T extends A<T>> setA(T a) {
this.a = a;
}
}
- КОНЕЦ ВОПРОСА -
Я заметил тенденцию ряда участников StackOverflow подходить к некоторым трудным вопросам с вопросом «почему вы делаете это в первую очередь?» Следующее является оправданием моего использования этого шаблона - вы можете заметить, что стандартная библиотека Java также использует рекурсивные self-типы в своем определении класса Enum: Enum<E extends Enum<E>>
. Этот вопрос также может быть задан как «как определить поле типа Enum<?>
.
Пример обоснования:
abstract class A<T extends A<T>> {
public abtract T self();
public B<T> bify(Bifyer bifyer) {
return bifyer.bify(self());
}
}
с подклассами:
class ASub1 extends A<ASub1> {
public ASub1 self() { return this; }
}
class ASub2 extends A<ASub2> {
public ASub2 self() { return this; }
}
привязан к параллельной иерархии классов:
abstract class B<T extends A<T>> {
}
class BSub1<T extends A<T>> implements B<T> { }
class BSub2<T extends A<T>> implements B<T> { }
//and others
И с генерацией экземпляров B, управляемых реализациями интерфейса Bifyer:
interface Bifyer {
B<ASub1> bify(ASub1 asub1);
B<ASub2> bify(ASub2 asub2);
}
Реализации этого интерфейса могут возвращать BSub1 или BSub2 для B. Это, по сути, применение шаблона Visitor, где Bifyer является посетителем, но в отличие от стандартного Visitor метод accept возвращает значение вместо void. Это обеспечивает модульную структуру, в которой могут быть указаны различные реализации Bifyer для обеспечения альтернативного поведения и типов возврата для метода Bify - скажем, по одному для каждого подкласса B.