Тип поля Java для значения универсально рекурсивного собственного типа? - PullRequest
4 голосов
/ 30 марта 2009

Учитывая иерархию классов, в которой базовый класс определяет рекурсивный тип:

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.

1 Ответ

4 голосов
/ 30 марта 2009

Если вы связали подстановочный знак ? ниже A, он должен работать:

public class B {
    private A<? extends A> a;

    public <T extends A<T>> void setA(T a) {
        this.a = a;
    }
}
...