a vantagem do generics nao seria exatamente ter uma tipagem mais forte nesses casos, em especial em classes containers? Algumas pessoas acham que e por compatibilidade binaria, mas nao e, porque a erasure de um metodo que recebe Te Object, e isso manteria a compatibilidade.Para entender melhor a decisao da Sun em cada uma dessas assinaturas de metodos voce sempre precisa levar em conta dois pontos: erasure e o uso do coringa. Antes vamos relembrar esses conceitos. Considere o codigo que imprime uma lista de objetos Comparable:void imprime(List<Comparable> lista) { for (Comparable c : lista) System.out.println(c); }Podemos passar para esse metodo uma List<Comparable>. E uma List<String>? Nao podemos, porque uma List<String> nao e uma List<Comparable>. Se isso fosse possivel, poderiamos adicionar objetos Comparable que nao apenas Strings dentro de umaList<String>, quebrando o contrato! Para resolver esse problema, mudamos a assinatura do nosso metodo para:void imprime(List<? extends Comparable> lista) { for (Comparable c : lista) System.out.println(c); }Agora podemos passar uma List<String>, pois isso e uma List<? extends Comparable>, isto e, uma lista de algum tipo que e compativel com Comparable. Porque entao nao usamos sempre esse idiomismo? Repare entao no seguinte metodo:public void adicionaString(List<Comparable> lista) { lista.add("caelum"); }Aqui temos o mesmo problema. Nao podemos receber como referencia uma List<String>! Vamos tentar aplicar o mesmo procedimento:public void adicionaString(List<? extends Comparable> lista) { lista.add("caelum"); // nao podemos invocar metodos que recebem o tipo // parametrizado (a menos que seja passado null) }O codigo acima nao compila! Isso ocorre porque List<? extends Comparable> pode receber uma lista de qualquer tipo que seja Comparable. Se isso fosse possivel, alguem poderia passar como argumento uma List<Integer>, e no fim do metodo esta lista de inteiro estaria contendo uma String, quebrando o contrato novamente!Como resolver esse problema? Nao tem como! Se voce recebe um objeto parametrizado pelo coringa ?, voce nunca podera invocar um metodo desse objeto, porque voce nao sabe qual o tipo que ele realmente aceita! A linguagem nao permite isso porque nao sabe exatamente o que voce vai fazer com esse objeto. Em alguns casos, semanticamente, apesar de um metodo receber um tipo parametrizado, nao tem problema ele receber um objeto que nao esteja de acordo com seu tipo parametrizado. Esse e exatamente o caso do metodo contains: nao haveria problema de passar um Integer para o contains de uma List<String>! Mas se contains recebesse E, o seguinte codigo nao compilaria:public boolean contemCaelum(List<? extends Comparable> lista) { return lista.contains("caelum"); // ok, recebe Object! }Essa situacao e a mesma para outros metodos, como o Collection.remove(Object), e oMap.get(Object). Ambos os casos seriam catastroficos se recebessem o tipo parametrizado como argumento.A API de colecoes traz assinaturas muito mais estranhas. Eu demorei bastante ate entender por completo a assinatura de alguns metodos da Collections, como por exemplo oCollections.min:public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll);em vez de simplesmente uma ingenua assinatura como:public static <T extends Comparable<T>> T min(Collection<T> coll);Fica ai o desafio para voce tambem queimar neuronios...