Declarações válidas com generics:
ArrayList list1 = new ArrayList<String>();
List list2 = new ArrayList<String>();
List<String> list3 = new ArrayList<String>();
List<?> list4 = new ArrayList<String>();
Observe, no entanto, que a primeira e segunda declaracao nos permitirá fazer:
list2.add(new Integer(6));
list1.add(new Object());
E, por outro lado, a terceira declaração não captura nenhum tipo, portanto, não podemos nenhum objeto para os métodos de list, a não ser null.
list4.add("hello world"); //não compila.
list4.add(null); //compila, mas o seu efeito é nulo.
As seguintes declarações são inválidas, não compilam (suponha que Dog estende Animal):
List<?> list5 = new ArrayList<? extends String>();
List<? extends Dog> list6 = new ArrayList<Integer>();
List<? super Animal> list7 = new ArrayList<Dog>();
A primeira não compila, pois a inicialização precisa ter por base uma classe específica. A segunda não compila pois list6 é declarado para receber qualquer tipo que estenda Dog, mas Integer não estende Dog ea terceira não compila pois list7 é declarado para receber qualquer classe que seja superclasse de Animal, inclusive Animal, mas Dog é subclasse de Animal.
Vejamos mais algumas declarações válidas:
ArrayList<? extends String> list2 = new ArrayList<String>();
ArrayList<? extends CharSequence> list3 = new ArrayList<String>();
ArrayList<? super String> list4 = new ArrayList<CharSequence>();
Repare, no entanto, que não podemos fazer:
list2.add("Hello!");//nao compila
list3.add("Hello!");//nao compila
Pois list3 e list2 foram declarados para receber qualquer classe que estenda String e CharSequence, respectivamente, e, como já vimos, quando isto acontece, o método add fica bloqueado. No entando, podemos perfeitamente fazer
list4.add("Hello");