Estou lendo sobre métodos genéricos do OracleDocGenericMethod . Estou bastante confuso sobre a comparação quando diz quando usar curingas e quando usar métodos genéricos. Citando do documento.
interface Collection<E> { public boolean containsAll(Collection<?> c); public boolean addAll(Collection<? extends E> c); }
Poderíamos ter usado métodos genéricos aqui:
interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c); // Hey, type variables can have bounds too! }
[…] Isso nos diz que o argumento de tipo está sendo usado para polimorfismo; seu único efeito é permitir que vários tipos de argumentos reais sejam usados em diferentes sites de chamada. Se for esse o caso, deve-se usar curingas. Os curingas são projetados para oferecer suporte à subtipagem flexível, que é o que estamos tentando expressar aqui.
Não achamos que o curinga (Collection<? extends E> c);
também suporta um tipo de polimorfismo? Então, por que o uso genérico do método não é considerado bom nisso?
Continuando adiante, afirma,
Métodos genéricos permitem que parâmetros de tipo sejam usados para expressar dependências entre os tipos de um ou mais argumentos para um método e / ou seu tipo de retorno. Se não houver essa dependência, um método genérico não deve ser usado.
O que isto significa?
Eles apresentaram o exemplo
class Collections { public static <T> void copy(List<T> dest, List<? extends T> src) { ... }
[...]
Poderíamos ter escrito a assinatura desse método de outra maneira, sem usar caracteres curinga:
class Collections { public static <T, S extends T> void copy(List<T> dest, List<S> src) { ... }
O documento desencoraja a segunda declaração e promove o uso da primeira sintaxe? Qual é a diferença entre a primeira e a segunda declaração? Ambos parecem estar fazendo a mesma coisa?
Alguém pode colocar luz nesta área.
?
. Você pode reescrevê-lo como `public static <T1 extends Number, T2 extends Number> void copy (Lista <T1> dest, List <T2> src) e, neste caso, fica óbvio o que está acontecendo.List
parâmetro de tipo using.List<T super Integer>
não é válido e não será compilado.<T extends X & Y>
-> múltiplos limites.Considere o seguinte exemplo de The Java Programming de James Gosling 4th edition abaixo, onde queremos mesclar 2 SinglyLinkQueue:
Ambos os métodos acima têm a mesma funcionalidade. Então, qual é preferível? A resposta é a segunda. Nas próprias palavras do autor:
"A regra geral é usar curingas quando possível, porque o código com curingas geralmente é mais legível que o código com vários parâmetros de tipo. Ao decidir se você precisa de uma variável de tipo, pergunte-se se essa variável de tipo é usada para relacionar dois ou mais parâmetros, ou relacionar um tipo de parâmetro com o tipo de retorno. Se a resposta for não, um curinga deve ser suficiente ".
Nota: No livro, apenas o segundo método é fornecido e o tipo de nome do parâmetro é S em vez de 'T'. O primeiro método não existe no livro.
fonte
Na sua primeira pergunta: significa que, se houver uma relação entre o tipo do parâmetro e o tipo de retorno do método, use um genérico.
Por exemplo:
Aqui você está extraindo parte do T seguindo um determinado critério. Se T for,
Long
seus métodos retornarãoLong
eCollection<Long>
; o tipo de retorno real depende do tipo de parâmetro, portanto, é útil e recomendado usar tipos genéricos.Quando não é esse o caso, você pode usar tipos de curingas:
Neste dois exemplos, seja qual for o tipo dos itens nas coleções, os tipos de retorno serão
int
eboolean
.Nos seus exemplos:
essas duas funções retornarão um booleano, independentemente dos tipos de itens nas coleções. No segundo caso, é limitado a instâncias de uma subclasse de E.
Segunda questão:
Este primeiro código permite que você passe um heterogêneo
List<? extends T> src
como parâmetro. Essa lista pode conter vários elementos de diferentes classes, desde que todos eles estendam a classe base T.se você tinha:
e
você poderia fazer
Por outro lado
restringe
List<S> src
a pertencer a uma classe S específica que é uma subclasse de T. A lista pode conter apenas elementos de uma classe (nesse caso, S) e nenhuma outra classe, mesmo que eles também implementem T. Você não seria capaz de usar meu exemplo anterior, mas poderia:fonte
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
Não é uma sintaxe válida. Você precisa instanciar o ArrayList sem limites.ArrayList(Collection<? extends E> c)
. Você pode me explicar por que você disse isso?O método curinga também é genérico - você pode chamá-lo com alguns tipos de tipos.
A
<T>
sintaxe define um nome de variável de tipo. Se uma variável de tipo tiver alguma utilidade (por exemplo, na implementação de métodos ou como uma restrição para outro tipo), faz sentido nomeá-la, caso contrário você pode usar?
como variável anônima. Então, parece apenas um atalho.Além disso, a
?
sintaxe não é evitável quando você declara um campo:fonte
Vou tentar responder à sua pergunta, uma por uma.
Não. O motivo é que o curinga limitado não possui um tipo de parâmetro definido. Isso é desconhecido. Tudo o que "sabe" é que a "contenção" é de um tipo
E
(o que for definido). Portanto, não é possível verificar e justificar se o valor fornecido corresponde ao tipo limitado.Portanto, não é sensato ter comportamentos polimórficos em caracteres curinga.
A primeira opção é melhor nesse caso, como
T
sempre é delimitada, esource
definitivamente terá valores (de incógnitas) que subclassesT
.Portanto, suponha que você queira copiar toda a lista de números, a primeira opção será
src
, essencialmente, pode aceitarList<Double>
,List<Float>
etc., pois há um limite superior para o tipo parametrizado encontrado emdest
.A segunda opção forçará você a vincular
S
todos os tipos que você deseja copiar, assimComo
S
é um tipo parametrizado que precisa de ligação.Eu espero que isso ajude.
fonte
<S extends T>
declara queS
é um tipo parametrizado que é uma subclasse deT
, portanto, requer um tipo parametrizado (sem curinga) que é uma subclasse deT
.Outra diferença que não está listada aqui.
Mas o seguinte resultará em erro no tempo de compilação.
fonte
Tanto quanto eu entendo, há apenas um caso de uso quando o curinga é estritamente necessário (ou seja, pode expressar algo que você não pode expressar usando parâmetros de tipo explícito). É quando você precisa especificar um limite inferior.
Além disso, os curingas servem para escrever um código mais conciso, conforme descrito pelas seguintes instruções no documento que você menciona:
fonte
Principalmente -> Curingas aplicam genéricos no nível de parâmetro / argumento de um método Não Genérico. Nota. Também pode ser executado em genericMethod por padrão, mas aqui em vez de? nós podemos usar o próprio T.
genéricos de pacotes;
O curinga SO tem seus casos de uso específicos como este.
fonte