O que o ponto de interrogação no parâmetro de tipo dos genéricos Java significa?

216

Este é um pequeno trecho de código retirado de alguns dos exemplos que acompanham o Stanford Parser. Estou desenvolvendo em Java há cerca de 4 anos, mas nunca tive uma compreensão muito forte do que esse estilo de código deveria indicar.

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

Não estou preocupado com os detalhes do código. O que me deixa confuso é o que exatamente a expressão genérica deve transmitir, em inglês.

Alguém pode me explicar isso?

sholsapp
fonte

Respostas:

226
? extends HasWord

significa "Uma classe / interface que se estende HasWord". Em outras palavras, HasWordele próprio ou qualquer um de seus filhos ... basicamente qualquer coisa que funcione com instanceof HasWordmais null.

Em termos mais técnicos, ? extends HasWordé um curinga limitado, coberto no Item 31 do Effective Java 3rd Edition , a partir da página 139. O mesmo capítulo da 2ª Edição está disponível online como PDF ; a parte dos curingas delimitados é o Item 28, começando na página 134.

Atualização: o link do PDF foi atualizado desde que a Oracle o removeu há um tempo. Agora ele aponta para a cópia apresentada pela Escola de Engenharia Eletrônica e Ciência da Computação da Universidade Queen Mary de Londres.

Atualização 2: Vamos detalhar um pouco mais o motivo pelo qual você deseja usar caracteres curinga.

Se você declarar um método cuja assinatura espera que você passe List<HasWord>, a única coisa que você pode passar é a List<HasWord>.

No entanto, se a assinatura for essa List<? extends HasWord>, você poderá passar uma List<ChildOfHasWord>.

Observe que há uma diferença sutil entre List<? extends HasWord>e List<? super HasWord>. Como Joshua Bloch colocou: PECS = produtor-estende, consumidor-super.

O que isso significa é que, se você estiver passando uma coleção da qual seu método extrai dados (ou seja, a coleção está produzindo elementos para o seu método), você deve usá-la extends. Se você estiver passando uma coleção à qual seu método adiciona dados (ou seja, a coleção está consumindo elementos criados por seu método), ela deve ser usada super.

Isso pode parecer confuso. No entanto, você pode vê-lo no comando List's sort(que é apenas um atalho para a versão de dois argumentos do Collections.sort). Em vez de pegar um Comparator<T>, ele realmente leva um Comparator<? super T>. Nesse caso, o comparador está consumindo os elementos do a Listfim de reordenar a própria lista.

Powerlord
fonte
17
"qualquer coisa que funcione com instanceof" - mais valores nulos. Lembre-se de que valores nulos retornam false para qualquer instância de verificação.
Eyal Schneider
12
Não esqueça de interfaces. O ? não precisa representar uma classe!
Mark Peters
9
List<HasWord>é exatamente o que você descreve: uma lista que contém objetos xpara os quais x instanceof HasWordretorna true e null. Você não precisa de um curinga para isso. O curinga significa que na verdade também pode ser uma lista de outro tipo, desde que esse tipo seja um subtipo de HasWord. (Desculpe tarde comentário.)
Paulo Ebermann
1
Link do PDF não parece estar funcionando, mas esta foi útil para mim: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062
É estranho, nunca recebi uma mensagem no ano passado quando o @ user1338062 postou e não sabia que o link do PDF não estava funcionando. Corrigido agora. Eu também adicionei um link para o próprio livro.
precisa saber é o seguinte
65

Um ponto de interrogação é um significante para 'qualquer tipo'. ?sozinho significa

Qualquer tipo de extensão Object(inclusive Object)

enquanto o seu exemplo acima significa

Qualquer tipo de extensão ou implementação HasWord(incluindo HasWordse HasWordfor uma classe não abstrata)

Jherico
fonte
2
então o que public Set<Class<?>> getClasses()significa? Qual a diferença Set<Class>? Eu estou olhando javax.ws.rs.core.Application.
abóbada
14

List<? extends HasWord>aceita quaisquer classes concretas que estendem o HasWord. Se você tem as seguintes classes ...

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

... o wordListSOMENTE pode conter uma lista de As ou Bs ou mistura de ambos, porque ambas as classes estendem o mesmo pai ou null(o que falha na verificação de instância HasWorld).

limc
fonte
8
Não apenas subclasses concretas, mas abstratas.
bloparod
2
Não só concreto e subclasses abstratas, mas também sub-interfaces: List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Mark Peters
2
Na verdade, você não precisa de curingas: List<HasWord>faz o mesmo para isso. O ponto aqui é que essa variável pode conter um List<A>ou List<B>tão bem como um List<HasWord>.
Paŭlo Ebermann 12/08
12

Talvez um exemplo artificial do "mundo real" ajudasse.

No meu local de trabalho, temos caixotes do lixo com sabores diferentes. Todas as caixas contêm lixo, mas algumas são especializadas e não levam todos os tipos de lixo. Então nós temos Bin<CupRubbish>e Bin<RecylcableRubbish>. O sistema de tipos precisa ter certeza de que não posso colocar o meu HalfEatenSandwichRubbishem nenhum desses tipos, mas ele pode entrar em uma lixeira geral Bin<Rubbish>. Se eu quisesse falar sobre um Bindos Rubbishquais pode ser especializado para não colocar lixo incompatível, então seria Bin<? extends Rubbish>.

(Nota: ? extendsnão significa somente leitura. Por exemplo, posso, com as devidas precauções, retirar um pedaço de lixo de uma lixeira de especialidade desconhecida e depois colocá-lo novamente em um local diferente.)

Não tenho certeza de quanto isso ajuda. Ponteiro para ponteiro na presença de polimorfismo não é totalmente óbvio.

Tom Hawtin - linha de orientação
fonte
5

Em inglês:

É Listde algum tipo que estende a classe HasWord, incluindoHasWord

Em geral, os ?genéricos in significam qualquer classe. E extends SomeClassespecifica que esse objeto deve se estender SomeClass(ou ser essa classe).

jjnguy
fonte
1
Na verdade, digite em vez de classe . Isso funciona tão bem para interfaces.
Paŭlo Ebermann 12/08/11