2009-08-10 4 views
5

У меня возникли проблемы с пониманием Java генериков, и я упростил этому примеруJava круговыми Дженерики

class A<T extends B> { 

    public void fun(T t) { 

    } 
} 

class B { 
    A a; 

    public void event() { 
     a.fun(this); 
    } 

} 

Проблема заключается в том, что это создает предупреждение, потому что А определяется внутри В, но А уже использует его как общий тип.

Мой первый инстинкт был бы в том, что мой дизайн неправильный, но в этом случае я не могу его изменить. A подобен коллекции, а B подобен узлу в коллекции, который пользователи должны переопределить. Некоторые события могут произойти в B, которые требуют отчетности обратно к родительскому А.

Но поскольку А определен обобщенно с B, как избежать предупреждения компиляции внутри B.event()

Благодаря

+0

Единственное предупреждение, которое я вижу, - это использование A как необработанного типа. Если это не предупреждение, о котором вы говорите, будьте более конкретным и сообщите нам, какой компилятор вы используете. – skaffman

ответ

11

Код

public class A<T extends B> { 
    public void fun(T t) { 
    } 
} 

public class B { 
    A<B> a; 

    public void event() { 
     a.fun(this); 
    } 
} 

Предупреждение побежденных.

Причина

Переменные типа A должны быть объявлены с помощью типа класса конкретных, как это было предложено родового класса подписи (A<T extends B>).

Разрешение

Хотя это разрешает предупреждение компилятора, основная проблема остается. Лоуренс дает отличное объяснение и решение основной проблемы.

13

проблема заключается в том, что вы используете необработанный тип по этой линии:

A a; 

вы должны указать тип для параметра тип A в (T).

Вы могли бы сделать что-то вроде этого:

A<B> a; 

а то А может и не быть общим на всех, если я понимание вашей постановки задачи. Вы, вероятно, хотите сделать что-то вроде этого:

class A<T> { 
    public void fun(T t) { 

    } 
} 

class B<T extends B<T>> { 
    A<B<T>> a; 
    public void event() { 
    a.fun(this); 
    } 
}  

или даже это:

class A<T extends B<? extends T>> { 
    public void fun(T t) { 

    } 
} 

class B<T extends B<T>> { 
    A<? super B<T>> a; 
    public void event() { 
    a.fun(this); 
    } 
} 

Есть несколько вариаций, между этими, которые, возможно, являются полезными, а также. Последний пример является наиболее общим (но, очевидно, и самым сложным).

class A<T extends B<? extends T>> гарантирует, что параметр типа A является B. Поскольку B сам по себе является общим и имеет этот параметр циклического типа, вам нужно сказать B<? extends T> (просто говоря, что T здесь не будет работать).

class B<T extends B<T>> как можно ближе к эмулированию «self type» в Java. Это позволяет B говорить о (почти) конкретном подтипе самого себя. При подклассификации B вы бы сказали что-то вроде «class C extends <B<C>>». Это полезно, потому что теперь тип C.a на самом деле A<? super B<C>>.

В последнем примере бит ? super полезен только в том случае, если вы планируете подключать B с помощью A, который не соответствует точному типу B. Думая в конкретных терминах, предположим, что у вас есть A<Shape> и Circle (который распространяется на Shape, который распространяется на B). Супер-подстановочный знак позволяет использовать их вместе. Без него вам понадобится A<Circle>, а не A<Shape> для вашего Circle.

+0

Это правильно, и отличный пример подстановочных знаков. –