Autor: Rafael Felício Fernandes | Publicado: 2011-12-09T02:42:00-08:00





A&nbsp;Collection&nbsp;genérica: métodos que recebem&nbsp;Object


Os desenvolvedores costumam levar um susto com as assinaturas dos métodos das interfaces do framework das coleções pós-java5: muito mais difícil do que o monte de&nbsp;Es,&nbsp;supers,&nbsp;extends,&nbsp;?s e&amp;s são os métodos que ainda recebem&nbsp;Object&nbsp;como argumento.

Um exemplo é o método&nbsp;contains. Apesar dele estar na interface parametrizada&nbsp;Collection&lt;E&gt;ele recebe&nbsp;Object&nbsp;em vez de&nbsp;E! Parece um paradoxo: a vantagem do generics não seria exatamente ter uma tipagem mais forte nesses casos, em especial em classes containers? Algumas pessoas acham que é por compatibilidade binária, mas não é, porque a&nbsp;erasure&nbsp;de um método que recebe&nbsp;Té&nbsp;Object, e isso manteria a compatibilidade.

Para entender melhor a decisão da Sun em cada uma dessas assinaturas de métodos você sempre precisa levar em conta dois pontos:&nbsp;erasure&nbsp;e o uso do coringa. Antes vamos relembrar esses conceitos. Considere o código que imprime uma lista de objetos&nbsp;Comparable:





void imprime(List&lt;Comparable&gt; lista) {

&nbsp;&nbsp;&nbsp;&nbsp;for (Comparable c : lista)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(c);

&nbsp;&nbsp;}






Podemos passar para esse método uma&nbsp;List&lt;Comparable&gt;. E uma&nbsp;List&lt;String&gt;? Não podemos, porque uma&nbsp;List&lt;String&gt;&nbsp;não é&nbsp;uma&nbsp;List&lt;Comparable&gt;. Se isso fosse possível, poderíamos adicionar objetos&nbsp;Comparable&nbsp;que não apenas&nbsp;Strings dentro de umaList&lt;String&gt;, quebrando o contrato! Para resolver esse problema, mudamos a assinatura do nosso método para:





void imprime(List&lt;? extends Comparable&gt; lista) {

&nbsp;&nbsp;&nbsp;for (Comparable c : lista)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(c);

&nbsp;}






Agora podemos passar uma&nbsp;List&lt;String&gt;, pois isso é uma&nbsp;List&lt;? extends Comparable&gt;, isto é, uma lista de algum tipo que é compatível com&nbsp;Comparable. Porque então não usamos sempre esse idiomismo? Repare então no seguinte método:





public void adicionaString(List&lt;Comparable&gt; lista) {

&nbsp;&nbsp;&nbsp;lista.add("caelum");

&nbsp;}






Aqui temos o mesmo problema. Não podemos receber como referência uma&nbsp;List&lt;String&gt;! Vamos tentar aplicar o mesmo procedimento:





public void adicionaString(List&lt;? extends Comparable&gt; lista) {

&nbsp;&nbsp;&nbsp;lista.add("caelum"); // não podemos invocar métodos que recebem o tipo

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// parametrizado (a menos que seja passado null)

&nbsp;}






O código acima não compila! Isso ocorre porque&nbsp;List&lt;? extends Comparable&gt;&nbsp;pode receber uma lista de qualquer tipo que seja&nbsp;Comparable. Se isso fosse possível, alguém poderia passar como argumento uma&nbsp;List&lt;Integer&gt;, e no fim do método esta lista de inteiro estaria contendo uma&nbsp;String, quebrando o contrato novamente!

Como resolver esse problema? Não tem como! Se você recebe um objeto parametrizado pelo coringa&nbsp;?, você nunca poderá invocar um método desse objeto, porque você não sabe qual o tipo que ele realmente aceita! A linguagem não permite isso porque não sabe exatamente o que você vai fazer com esse objeto. Em alguns casos, semanticamente, apesar de um método receber um tipo parametrizado, não tem problema ele receber um objeto que não esteja de acordo com seu tipo parametrizado. Esse é exatamente o caso do método&nbsp;contains: não haveria problema de passar um&nbsp;Integer&nbsp;para o&nbsp;contains&nbsp;de uma&nbsp;List&lt;String&gt;! Mas se contains recebesse&nbsp;E, o seguinte código não compilaria:





public boolean contemCaelum(List&lt;? extends Comparable&gt; lista) {

&nbsp;&nbsp;&nbsp;return lista.contains("caelum"); // ok, recebe Object!

&nbsp;}






Essa situação é a mesma para outros métodos, como o&nbsp;Collection.remove(Object), e oMap.get(Object). Ambos os casos seriam catastróficos se recebessem o tipo parametrizado como argumento.

A API de coleções traz assinaturas muito mais estranhas. Eu demorei bastante até entender por completo a assinatura de alguns métodos da&nbsp;Collections, como por exemplo oCollections.min:





public static &lt;T extends Object &amp; Comparable&lt;? super T&gt;&gt; T

&nbsp;&nbsp;&nbsp;&nbsp;min(Collection&lt;? extends T&gt; coll);






em vez de simplesmente uma ingênua assinatura como:





public static &lt;T extends Comparable&lt;T&gt;&gt; T min(Collection&lt;T&gt; coll);






Fica aí o desafio para você também queimar neurônios…