Qual é a diferença entre 'E', 'T' e '?' para genéricos Java?


261

Me deparei com código Java como este:

public interface Foo<E> {}

public interface Bar<T> {}

public interface Zar<?> {}

Qual é a diferença entre os três itens acima e como eles chamam esse tipo de declaração de classe ou interface em Java?


1
Duvido que haja alguma diferença. Eu acho que é apenas um nome para esse parâmetro de tipo. E o último é válido?
CodesInChaos

Respostas:


231

Bem, não há diferença entre os dois primeiros - eles estão apenas usando nomes diferentes para o parâmetro type ( Eou T).

A terceira não é uma declaração válida - ?é usada como curinga que é usada ao fornecer um argumento de tipo , por exemplo, List<?> foo = ...significa que foose refere a uma lista de algum tipo, mas não sabemos o que.

Tudo isso é genérico , que é um tópico bastante grande. Você pode aprender sobre isso através dos seguintes recursos, embora haja mais disponíveis, é claro:


1
Parece que o link para o PDF está quebrado. Encontrei o que parece ser uma cópia aqui , mas não posso estar 100% certo, pois não sei como era o original.
John

2
@ John: Sim, é esse. Irá editar um link em, quer que um ou um Oracle um ...
Jon Skeet

Existe algo além de T, E e? usado em genéricos? Se sim, o que são e o que significam?
sofs1

1
@ sofs1: Não há nada de especial Te E- eles são apenas identificadores. Você poderia escrever KeyValuePair<K, V>por exemplo. ?tem um significado especial.
precisa

215

É mais convenção do que qualquer outra coisa.

  • T deve ser um tipo
  • Edeve ser um elemento ( List<E>: uma lista de elementos)
  • Ké a chave (em a Map<K,V>)
  • V é Value (como valor de retorno ou valor mapeado)

Eles são totalmente intercambiáveis ​​(não obstante os conflitos na mesma declaração).


20
A letra entre <> é apenas um nome. O que você descreve em sua resposta são apenas convenções. Nem precisa ser uma única letra maiúscula; você pode usar qualquer nome que desejar, assim como pode dar classes, variáveis ​​etc. qualquer nome que desejar.
Jesper

Uma descrição mais detalhada e clara está disponível neste artigo oracle.com/technetwork/articles/java/…
fgul

6
Você não explicou o ponto de interrogação. Votado.
Shinzou 7/05/19

129

As respostas anteriores explicam os parâmetros de tipo (T, E etc.), mas não explicam o caractere curinga "?" Ou as diferenças entre eles; portanto, abordarei isso.

Primeiro, só para esclarecer: os parâmetros curinga e tipo não são os mesmos. Onde os parâmetros de tipo definem um tipo de variável (por exemplo, T) que representa o tipo de um escopo, o curinga não: o curinga apenas define um conjunto de tipos permitidos que você pode usar para um tipo genérico. Sem qualquer delimitação ( extendsou super), o curinga significa "use qualquer tipo aqui".

O curinga sempre se encontra entre colchetes angulares e só tem significado no contexto de um tipo genérico:

public void foo(List<?> listOfAnyType) {...}  // pass a List of any type

Nunca

public <?> ? bar(? someType) {...}  // error. Must use type params here

ou

public class MyGeneric ? {      // error
    public ? getFoo() { ... }   // error
    ...
}

Fica mais confuso onde eles se sobrepõem. Por exemplo:

List<T> fooList;  // A list which will be of type T, when T is chosen.
                  // Requires T was defined above in this scope
List<?> barList;  // A list of some type, decided elsewhere. You can do
                  // this anywhere, no T required.

Há muita sobreposição no que é possível com as definições de método. A seguir, são funcionalmente idênticos:

public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething)  {...}

Então, se houver sobreposição, por que usar um ou outro? Às vezes, é honestamente apenas estilo: algumas pessoas dizem que se você não precisar de um parâmetro de tipo, você deve usar um curinga apenas para tornar o código mais simples / mais legível. Uma diferença principal que expliquei acima: parâmetros de tipo definem uma variável de tipo (por exemplo, T) que você pode usar em outro lugar no escopo; o curinga não. Caso contrário, existem duas grandes diferenças entre os parâmetros de tipo e o curinga:

Parâmetros de tipo podem ter várias classes delimitadoras; o curinga não pode:

public class Foo <T extends Comparable<T> & Cloneable> {...}

O curinga pode ter limites mais baixos; params de tipo não podem:

public void bar(List<? super Integer> list) {...}

No exemplo acima, List<? super Integer>define Integercomo um limite inferior no curinga, o que significa que o tipo de Lista deve ser Inteiro ou um supertipo de Inteiro. O limite de tipo genérico está além do que quero abordar em detalhes. Em resumo, permite definir quais tipos podem ser de um tipo genérico. Isso torna possível o tratamento de genéricos polimorficamente. Por exemplo, com:

public void foo(List<? extends Number> numbers) {...}

Você pode passar um List<Integer>, List<Float>, List<Byte>, etc para numbers. Sem delimitação de tipos, isso não funcionará - é assim que os genéricos são.

Finalmente, aqui está uma definição de método que usa o curinga para fazer algo que eu acho que você não pode fazer de outra maneira:

public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
    numberSuper.add(elem);
}

numberSuperpode ser uma lista de números ou qualquer supertipo de número (por exemplo, List<Object>) e elemdeve ser um número ou qualquer subtipo. Com todas as delimitações, o compilador pode ter certeza de que .add()é tipicamente seguro .


"public void foo (List <? extends Number> numbers) {...}" deve "extends" ser "super"?
1a1a11a 06/06/2015

1
Não. O objetivo desse exemplo é mostrar uma assinatura que suporte polimorficamente uma lista de números e os subtipos de número. Para isso, você usa "estende". Ou seja, "me passe uma lista de números ou qualquer coisa que estenda o número" (lista <Integer>, lista <Float>, qualquer que seja). Um método como esse pode percorrer a lista e, para cada elemento, "e", executar, por exemplo, e.floatValue (). Não importa que subtipo (extensão) de Número você passe - você sempre poderá ".floatValue ()", porque .floatValue () é um método de Number.
precisa

No seu último exemplo, "List <? Super Number>" poderia simplesmente ser "List <Number>", pois o método não permite nada mais genérico.
jessarah

@jessarah nope. Talvez meu exemplo não esteja claro, mas mencionei no exemplo que adder () poderia receber uma List <Object> (Object é uma superclasse de Number). Se você quiser fazer isso, deve ter a assinatura "List <? Super Number>". Esse é exatamente o ponto de "super" aqui.
Hawkeye Parker

2
Essa resposta é muito boa para explicar as diferenças entre caracteres curinga e parâmetros de tipo; deve haver uma pergunta dedicada a essa resposta. Ultimamente, estou me aprofundando mais nos genéricos e essa resposta me ajudou muito na organização, muitas informações precisas em resumo, obrigado!
Testo Testini

27

Uma variável de tipo, <T>, pode ser qualquer tipo não primitivo que você especificar: qualquer tipo de classe, qualquer tipo de interface, qualquer tipo de matriz ou mesmo outra variável de tipo.

Os nomes dos parâmetros de tipo mais usados ​​são:

  • Elemento E (usado extensivamente pelo Java Collections Framework)
  • K - Key
  • N - Número
  • Tipo T
  • V - Valor

No Java 7, é permitido instanciar assim:

Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6

3

Os nomes dos parâmetros de tipo mais usados ​​são:

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

Você verá esses nomes usados ​​em toda a API do Java SE


2

O compilador fará uma captura para cada curinga (por exemplo, ponto de interrogação na Lista) quando compuser uma função como:

foo(List<?> list) {
    list.put(list.get()) // ERROR: capture and Object are not identical type.
}

No entanto, um tipo genérico como V ficaria bem e o tornaria um método genérico :

<V>void foo(List<V> list) {
    list.put(list.get())
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.