Que signifie le point d'interrogation dans le paramètre de type Java Generics?

216

Il s'agit d'un petit extrait de code extrait de certains des exemples qui accompagnent l'analyseur Stanford. Je développe en Java depuis environ 4 ans, mais je n'ai jamais eu une très bonne compréhension de ce que ce style de code est censé indiquer.

List<? extends HasWord> wordList = toke.tokenize();

Je ne m'inquiète pas des détails du code. Ce qui m'embrouille, c'est exactement ce que l'expression générique est censée transmettre, en anglais.

Quelqu'un peut m'expliquer cela?

sholsapp
la source

Réponses:

226
? extends HasWord

signifie "Une classe / interface qui s'étend HasWord." En d'autres termes, HasWordlui - même ou l'un de ses enfants ... essentiellement tout ce qui fonctionnerait avec instanceof HasWordplus null.

En termes plus techniques, il ? extends HasWords'agit d'un caractère générique délimité, couvert au point 31 de la version Java effective 3e , à partir de la page 139. Le même chapitre de la 2e édition est disponible en ligne au format PDF ; la partie sur les caractères génériques délimités est le point 28 à partir de la page 134.

Mise à jour: le lien PDF a été mis à jour depuis que Oracle l'a supprimé il y a quelque temps. Il pointe maintenant vers la copie hébergée par la School of Electronic Engineering and Computer Science de l'Université Queen Mary de Londres.

Mise à jour 2: permet d'entrer dans un peu plus de détails sur les raisons pour lesquelles vous souhaitez utiliser des caractères génériques.

Si vous déclarez une méthode dont la signature s'attend à ce que vous passiez List<HasWord>, alors la seule chose que vous pouvez transmettre est a List<HasWord>.

Cependant, si cette signature l'était, List<? extends HasWord>vous pourriez passer un à la List<ChildOfHasWord>place.

Notez qu'il existe une différence subtile entre List<? extends HasWord>et List<? super HasWord>. Comme l'a dit Joshua Bloch: PECS = producteur-étend, consommateur-super.

Cela signifie que si vous transmettez une collection à partir de laquelle votre méthode extrait des données (c'est-à-dire que la collection produit des éléments pour votre méthode), vous devez utiliser extends. Si vous transmettez une collection à laquelle votre méthode ajoute des données (c'est-à-dire que la collection consomme des éléments créés par votre méthode), elle doit utiliser super.

Cela peut sembler déroutant. Cependant, vous pouvez le voir dans Listla sortcommande de (qui n'est qu'un raccourci vers la version à deux arguments de Collections.sort). Au lieu de prendre un Comparator<T>, il faut en fait un Comparator<? super T>. Dans ce cas, le comparateur consomme les éléments de Listafin de réorganiser la liste elle-même.

Powerlord
la source
17
"tout ce qui fonctionnerait avec instanceof" - plus les valeurs nulles. N'oubliez pas que les valeurs nulles retournent false pour toute instance de vérification.
Eyal Schneider
12
N'oubliez pas les interfaces. Le ? n'a pas à représenter une classe!
Mark Peters
9
List<HasWord>est exactement ce que vous décrivez: une liste qui contient tous les objets xpour lesquels x instanceof HasWordrenvoie vrai, et null. Vous n'avez pas besoin d'un caractère générique pour cela. Le caractère générique signifie qu'il peut également s'agir d'une liste d'un autre type, tant que ce type est un sous-type de HasWord. (Désolé pour le commentaire tardif.)
Paŭlo Ebermann
1
Le lien PDF ne semble pas fonctionner, mais cela m'a été utile: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062
C'est bizarre, je n'ai jamais reçu de message l'année dernière lorsque @ user1338062 a posté et je ne savais pas que le lien PDF ne fonctionnait pas. Fixé maintenant. J'ai également ajouté un lien vers le livre lui-même.
Powerlord
65

Un point d'interrogation est un signifiant pour «tout type». ?seul signifie

Tout type d'extension Object(y compris Object)

alors que votre exemple ci-dessus signifie

Tout type d'extension ou d'implémentation HasWord(y compris HasWordsi HasWordest une classe non abstraite)

Jherico
la source
2
alors qu'est-ce que cela public Set<Class<?>> getClasses()signifie? Quelle est la différence avec Set<Class>? Je regarde javax.ws.rs.core.Application.
saut
14

List<? extends HasWord>accepte toutes les classes concrètes qui étendent HasWord. Si vous avez les cours suivants ...

public class A extends HasWord { .. }
public class B extends HasWord { .. }
public class C { .. }
public class D extends SomeOtherWord { .. }

... le wordListpeut contenir UNIQUEMENT une liste de As ou Bs ou un mélange des deux parce que les deux classes étendent le même parent ou null(ce qui échoue lors de la vérification des instances HasWorld).

limc
la source
8
Non seulement des sous-classes concrètes, mais aussi abstraites.
bloparod
2
Non seulement les sous - classes abstraites et concrètes, mais aussi des sous-interfaces: List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Mark Peters
2
Vous n'avez pas besoin de caractères génériques pour cela, en fait: List<HasWord>fait aussi bien pour cela. Le point ici est que cette variable peut contenir un List<A>ou List<B>ainsi qu'un a List<HasWord>.
Paŭlo Ebermann
12

Peut-être qu'un exemple artificiel de "monde réel" aiderait.

Sur mon lieu de travail, nous avons des poubelles qui se déclinent en différentes saveurs. Tous les bacs contiennent des déchets, mais certains bacs sont spécialisés et ne prennent pas tous les types de déchets. Nous avons donc Bin<CupRubbish>et Bin<RecylcableRubbish>. Le système de type doit s'assurer que je ne peux pas mettre mon HalfEatenSandwichRubbishdans l'un de ces types, mais il peut aller dans une poubelle générale Bin<Rubbish>. Si je voulais parler de l'un Bind'entre eux Rubbishqui pourrait être spécialisé pour que je ne puisse pas jeter des ordures incompatibles, ce serait le cas Bin<? extends Rubbish>.

(Remarque: ? extendsne signifie pas en lecture seule. Par exemple, je peux avec des précautions appropriées sortir un morceau de détritus d'une poubelle de spécialité inconnue et le remettre plus tard dans un endroit différent.)

Je ne sais pas combien cela aide. Pointeur vers pointeur en présence de polymorphisme n'est pas entièrement évident.

Tom Hawtin - sellerie
la source
5

En anglais:

C'est un Listtype qui étend la classe HasWord, y comprisHasWord

En général, les ?termes génériques désignent n'importe quelle classe. Et le extends SomeClassspécifie que cet objet doit s'étendre SomeClass(ou être cette classe).

jjnguy
la source
1
En fait, tapez au lieu de la classe . Cela fonctionne aussi bien pour les interfaces.
Paŭlo Ebermann