Quero tentar detalhar a resposta do @DerMike para explicar:
Primeiro, o apagamento de tipo não significa que o JDK elimina informações de tipo em tempo de execução. É um método para permitir que a verificação do tipo em tempo de compilação e a compatibilidade do tipo em tempo de execução coexistam no mesmo idioma. Como esse bloco de código implica, o JDK retém as informações de tipo apagadas - elas não estão associadas a conversões e outras coisas verificadas.
Segundo, isso fornece informações de tipo genérico para uma classe genérica exatamente um nível acima da hierarquia do tipo concreto que está sendo verificado - ou seja, uma classe pai abstrata com parâmetros de tipo genérico pode encontrar os tipos concretos correspondentes aos seus parâmetros de tipo para uma implementação concreta de si mesma. que herda diretamente dele. Se essa classe não fosse abstrata e instanciada, ou a implementação concreta estivesse em dois níveis abaixo, isso não funcionaria (embora um pouco de brincadeira pudesse fazer com que se aplicasse a qualquer número predeterminado de níveis além de um ou até a classe mais baixa com X parâmetros de tipo genérico, etc.).
Enfim, para a explicação. Aqui está o código novamente, separado em linhas para facilitar a referência:
1 # Classe genericParameter0OfThisClass =
2 # (classe)
3 # ((ParameterizedType)
4 # getClass ()
5 # .getGenericSuperclass ())
6 # .getActualTypeArguments () [0];
Sejam 'nós' a classe abstrata com tipos genéricos que contêm esse código. Lendo isso de dentro para fora:
- A linha 4 obtém a instância atual da classe concreta 'Class. Isso identifica o tipo concreto de nosso descendente imediato.
- A linha 5 obtém o supertipo dessa classe como um Tipo; estes somos nós. Como somos do tipo paramétrico, podemos converter-nos com segurança no ParameterizedType (linha 3). A chave é que, quando Java determina esse objeto Type, ele usa informações de tipo presentes no filho para associar informações de tipo aos nossos parâmetros de tipo na nova instância ParameterizedType. Portanto, agora podemos acessar tipos concretos para nossos genéricos.
- A linha 6 obtém a matriz de tipos mapeada em nossos genéricos, na ordem declarada no código da classe. Neste exemplo, extraímos o primeiro parâmetro. Isso volta como um tipo.
- A linha 2 lança o tipo final retornado para uma classe. Isso é seguro porque sabemos quais tipos nossos parâmetros de tipo genérico são capazes de aceitar e podemos confirmar que todos serão classes (não sei como em Java seria possível obter um parâmetro genérico que não possui uma instância de Classe associado a ele, na verdade).
... e é isso mesmo. Por isso, inserimos informações de tipo de nossa própria implementação concreta em nós mesmos e as usamos para acessar um identificador de classe. poderíamos duplicar o getGenericSuperclass () e subir dois níveis, ou eliminar o getGenericSuperclass () e obter valores para nós mesmos como um tipo concreto (ressalva: não testei esses cenários, eles ainda não vieram para mim).
Fica complicado se seus filhos concretos estão a um número arbitrário de saltos de distância, ou se você é concreto e não final, e especialmente complicado se você espera que algum de seus filhos (com profundidade variada) tenha seus próprios genéricos. Mas você geralmente pode projetar em torno dessas considerações, e isso o leva a maior parte do caminho.
Espero que isso tenha ajudado alguém! Eu reconheço que este post é antigo. Provavelmente cortarei esta explicação e a guardarei para outras perguntas.