¿Qué significa el signo de interrogación en el parámetro tipo de genéricos de Java?

216

Este es un pequeño fragmento de código tomado de algunos de los ejemplos que acompañan al Stanford Parser. He estado desarrollando en Java durante aproximadamente 4 años, pero nunca he tenido una comprensión muy sólida de lo que se supone que indica este estilo de código.

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

No me preocupan los detalles del código. Lo que me confunde es qué se supone que debe transmitir exactamente la expresión genérica, en inglés.

¿Alguien puede explicarme esto?

sholsapp
fuente

Respuestas:

226
? extends HasWord

significa "Una clase / interfaz que se extiende HasWord". En otras palabras, HasWordsí mismo o cualquiera de sus hijos ... básicamente cualquier cosa que funcione con instanceof HasWordplus null.

En términos más técnicos, ? extends HasWordes un comodín acotado, cubierto en el Artículo 31 de Effective Java 3rd Edition , que comienza en la página 139. El mismo capítulo de la 2da Edición está disponible en línea como PDF ; la parte de los comodines delimitados es el Artículo 28 que comienza en la página 134.

Actualización: el enlace PDF se actualizó desde que Oracle lo eliminó hace un tiempo. Ahora apunta a la copia presentada por la Escuela de Ingeniería Electrónica e Informática de la Universidad Queen Mary de Londres.

Actualización 2: Veamos un poco más de detalles sobre por qué querría usar comodines.

Si declara un método cuya firma espera que pase List<HasWord>, entonces lo único que puede pasar es a List<HasWord>.

Sin embargo, si dicha firma era, List<? extends HasWord>entonces podría pasar un en su List<ChildOfHasWord>lugar.

Tenga en cuenta que hay una sutil diferencia entre List<? extends HasWord>y List<? super HasWord>. Como dijo Joshua Bloch: PECS = productor-se extiende, consumidor-super.

Lo que esto significa es que si está pasando una colección de la cual su método extrae datos (es decir, la colección está produciendo elementos para su método), debe usarla extends. Si está pasando una colección a la que su método agrega datos (es decir, la colección está consumiendo elementos que crea su método), debe usarla super.

Esto puede sonar confuso. Sin embargo, puede verlo en Listel sortcomando de (que es solo un acceso directo a la versión de dos argumentos de Collections.sort). En lugar de tomar un Comparator<T>, en realidad se necesita un Comparator<? super T>. En este caso, el Comparador está consumiendo los elementos del Listpara reordenar la Lista misma.

Powerlord
fuente
17
"cualquier cosa que funcione con instanceof" - más valores nulos. Recuerde que los valores nulos devuelven falso para cualquier instancia de verificación.
Eyal Schneider
12
No olvides las interfaces. Los ? no tiene que representar una clase!
Mark Peters
99
List<HasWord>es exactamente lo que usted describe: una lista que contiene cualquier objeto xpara el cual x instanceof HasWorddevuelve verdadero, y null. No necesitas un comodín para esto. El comodín significa que en realidad también puede ser una lista de otro tipo, siempre que este tipo sea un subtipo de HasWord. (Perdón por el comentario tardío.)
Paŭlo Ebermann
1
El enlace PDF no parece funcionar, pero esto me fue útil: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062
Eso es extraño, nunca recibí un mensaje el año pasado cuando @ user1338062 publicó y no sabía que el enlace PDF no funcionaba. Corregido ahora. También agregué un enlace al libro en sí.
Powerlord
65

Un signo de interrogación es un significante para 'cualquier tipo'. ?solo significa

Cualquier tipo que se extienda Object(incluido Object)

mientras su ejemplo anterior significa

Cualquier tipo que se extienda o implemente HasWord(incluso HasWordsi HasWordes una clase no abstracta)

Jherico
fuente
2
Entonces, ¿qué public Set<Class<?>> getClasses()significa? ¿Cuál es la diferencia con Set<Class>? Estoy mirando javax.ws.rs.core.Application.
bóveda
14

List<? extends HasWord>acepta cualquier clase concreta que extienda HasWord. Si tienes las siguientes clases ...

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

... la wordListlata SOLO puede contener una lista de As o Bs o una mezcla de ambas porque ambas clases extienden el mismo padre o null(que falla la instancia de las comprobaciones HasWorld).

limc
fuente
8
No solo concreto, sino también subclases abstractas.
bloparod
2
No sólo concreto y subclases abstractos, sino también sub-interfaces: List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Mark Peters
2
No necesitas comodines para esto, en realidad: List<HasWord>funciona igual de bien para esto. El punto aquí es que esta variable puede contener ao List<A>o List<B>a List<HasWord>.
Paŭlo Ebermann
12

Quizás un ejemplo artificial del "mundo real" ayudaría.

En mi lugar de trabajo tenemos contenedores de basura que vienen en diferentes sabores. Todos los contenedores contienen basura, pero algunos contenedores son especializados y no toman todo tipo de basura. Entonces tenemos Bin<CupRubbish>y Bin<RecylcableRubbish>. El sistema de tipos necesita asegurarse de que no puedo colocar mi HalfEatenSandwichRubbishen ninguno de estos tipos, pero puede ir a un contenedor de basura general Bin<Rubbish>. Si quisiera hablar sobre uno Binde los Rubbishcuales puede estar especializado para no poder poner basura incompatible, entonces sería Bin<? extends Rubbish>.

(Nota: ? extendsno significa solo lectura. Por ejemplo, con las precauciones adecuadas puedo sacar un pedazo de basura de un contenedor de especialidad desconocida y luego volver a colocarlo en un lugar diferente).

No estoy seguro de cuánto ayuda eso. Puntero a puntero en presencia de polimorfismo no es del todo obvio.

Tom Hawtin - tackline
fuente
5

En inglés:

Es Listde algún tipo que extiende la clase HasWord, incluyendoHasWord

En general, el término ?genérico significa cualquier clase. Y extends SomeClassespecifica que ese objeto debe extenderse SomeClass(o ser esa clase).

jjnguy
fuente
1
En realidad, escriba en lugar de clase . Esto funciona igual de bien para las interfaces.
Paŭlo Ebermann