Eu acho que outras respostas disseram isso em algum grau, mas não muito claramente. Um dos problemas com 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 para que você não consiga descobrir isso. Como alguns afirmaram ArrayList<Integer>
parece o mesmo que ArrayList<String>
do ponto de vista da reflexão.
Isso pode não importar para o usuário do Generics, mas digamos que queremos criar uma estrutura sofisticada que use 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 queremos que uma fábrica de genéricos crie uma instância de MySpecialObject
, porque esse é o tipo genérico concreto que declaramos para essa instância. Bem, 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 seus tipos genéricos, porque o compilador o aceitou no binário. Com rasuras, o Java não pode fazer isso.