Eu acho que outras respostas disseram isso em algum grau, mas não muito claramente. Um dos problemas com os genéricos é perder os tipos genéricos durante o processo de reflexão. Então, por exemplo:
List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();
Infelizmente, os tipos retornados não podem dizer que os tipos genéricos de arr são String. É uma diferença sutil, mas é importante. Como o arr é criado no tempo de execução, os tipos genéricos são apagados no tempo de execução, portanto você não pode descobrir isso. Como alguns afirmam, ArrayList<Integer>
parece o mesmo ArrayList<String>
do ponto de vista da reflexão.
Isso pode não importar para o usuário do Generics, mas digamos que desejássemos criar uma estrutura sofisticada que usasse a reflexão para descobrir coisas sofisticadas sobre como o usuário declarou os tipos genéricos concretos de uma instância.
Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();
Digamos que queríamos que uma fábrica genérica criasse uma instância, MySpecialObject
porque esse é o tipo genérico concreto que declaramos para essa instância. A classe Factory não pode se interrogar para descobrir o tipo concreto declarado para esta instância porque o Java os apagou.
Nos genéricos do .Net, você pode fazer isso porque, em tempo de execução, o objeto sabe que são tipos genéricos, porque o compilador o inseriu no binário. Com apagamentos, o Java não pode fazer isso.