2009-08-10 8 views
5

Je vais avoir un problème de compréhension génériques Java et j'ai simplifié à cet exempleJava Generics circulaires

class A<T extends B> { 

    public void fun(T t) { 

    } 
} 

class B { 
    A a; 

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

} 

Le problème est que cela génère un avertissement parce que A est défini à l'intérieur B, mais A est déjà l'utiliser en tant que type générique.

Mon premier instinct serait que mon design est faux, mais dans ce cas je ne peux pas le changer. A est comme une collection et B est comme un nœud de la collection que les utilisateurs sont censés remplacer. Certains événements peuvent se produire en B qui nécessitent de faire rapport au parent A.

Mais depuis A est défini génériquement avec B, comment puis-je éviter l'avertissement de compilation à l'intérieur B.event()

Merci

+0

Le seul avertissement que je peux voir est l'utilisation de A comme un type brut. Si ce n'est pas l'avertissement dont vous parlez, veuillez être plus précis, et dites-nous quel compilateur vous utilisez. – skaffman

Répondre

11

code

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

public class B { 
    A<B> a; 

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

L'avertissement est vaincu.

Raison

Les variables de type A doivent être déclarés en utilisant un type de classe spécifique , comme le suggère la signature générique de classe (A<T extends B>).

Résolution

Bien que cela résout l'avertissement du compilateur, le problème sous-jacent reste. Laurence fournit une excellente explication et une solution au problème principal.

13

le problème est que vous utilisez un type brut sur cette ligne:

A a; 

vous devez spécifier un type pour le paramètre de type de a (T).

Vous pourriez faire quelque chose comme ceci:

A<B> a; 

mais A pourrait aussi bien ne pas être générique du tout, si je comprends bien votre énoncé du problème. Vous voulez sans doute faire quelque chose comme ceci:

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

    } 
} 

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

ou même ceci:

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); 
    } 
} 

Il y a quelques variations dans-entre ceux-ci qui sont peut-être utiles. Le dernier exemple est le plus générique (mais évidemment aussi le plus compliqué). Le class A<T extends B<? extends T>> fait en sorte que le paramètre de type à A soit un B. Puisque B est lui-même générique, et a ce paramètre de type cyclique, vous finissez par avoir besoin de dire B<? extends T> (en disant simplement que T ne fonctionnera pas ici).

Le class B<T extends B<T>> est le plus proche possible de l'émulation d'un "type self" en Java. Cela permet à B de parler du sous-type (presque) concret de lui-même. En sous-classant B, vous diriez quelque chose comme "class C extends <B<C>>". Ceci est utile parce que maintenant le type de C.a est réellement A<? super B<C>>.

Le bit ? super dans le dernier exemple n'est utile que si vous envisagez de connecter un B avec un A qui ne correspond pas exactement au même type de B. Penser en termes concrets, supposons que vous avez eu un A<Shape> et un Circle (qui s'étend Shape qui s'étend B). Le super-caractère générique vous permet de les utiliser ensemble. Sans cela, vous aurez besoin d'un A<Circle> plutôt qu'un A<Shape> pour votre Circle.

+0

Ceci est correct, et un excellent exemple de caractères génériques. –