Bem, a motivação (compatibilidade com versões anteriores) é uma vantagem e uma desvantagem. É uma desvantagem, porque todos nós preferimos ter tipos reificáveis, mas o preço a pagar foi alto. Considere as opções de design em C #. Eles têm tipos reificáveis, mas agora eles têm APIs duplicadas. Então, imagine uma API Java em que também tivemos APIs duplicadas para cada classe parametrizada. Agora, imagine-se portando milhares de linhas de código das classes herdadas para as novas classes genéricas. Agora, quem não consideraria as APIs duplicadas uma desvantagem? Mas ei, eles têm tipos reificáveis!
Portanto, a principal motivação foi "evolução, não revolução". E logicamente, toda decisão tem trocas.
Além das outras desvantagens mencionadas, também podemos acrescentar o fato de que o apagamento de tipo pode ser difícil de raciocinar no momento da compilação, pois não é evidente que determinados tipos serão removidos e isso leva a erros muito estranhos e difíceis de encontrar.
A existência de métodos de ponte ( métodos compilados gerados sintaticamente para manter a compatibilidade binária) também pode ser vista como desvantagem. E essas podem ser precisamente uma das razões dos erros que mencionei no parágrafo anterior.
As principais desvantagens decorrem do fato já óbvio de que existe uma única classe e não várias para tipos genéricos. Como outro exemplo, considere que a sobrecarga de um método com a mesma classe genérica falha em Java:
public void doSomething(List<One>);
public void doSomething(List<Two>);
Algo que pode ser visto como uma desvantagem de tipos reificáveis (pelo menos em C #) é o fato de eles causarem explosão de código . Por exemplo, List<int>
é uma classe e a List<double>
é outra totalmente diferente, pois é a List<string>
e a List<MyType>
. Portanto, as classes precisam ser definidas em tempo de execução, causando uma explosão de classes e consumindo recursos valiosos enquanto são gerados.
Quanto ao fato de não ser possível definir a new T()
em Java, mencionado em outra resposta, também é interessante considerar que isso não é apenas uma questão de apagamento de tipo. Também requer a existência de um construtor padrão, por isso o C # requer uma "nova restrição" para isso. (Veja Por que o novo T () não é possível em Java , por Alex Buckley).
T
s, você recebe apenas uma cópia doClass<T>
código para todos osT
s; além de uma cópia adicional para cada tipo de valorT
realmente usado.A desvantagem do apagamento de tipo é que você não sabe em tempo de execução o tipo do genérico. Isso implica que você não pode aplicar reflexão neles e não pode instancia-los em tempo de execução.
Você não pode fazer algo assim em Java:
Existe uma solução alternativa para isso, mas isso requer mais código. A vantagem, como você mencionou, é a compatibilidade com versões anteriores.
fonte
Outra vantagem dos genéricos apagados é que diferentes linguagens que são compiladas na JVM empregam estratégias diferentes para genéricos, por exemplo, site de definição do Scala versus covariância do site de uso do Java. Também os genéricos mais sofisticados do Scala teriam sido mais difíceis de suportar nos tipos reificados do .Net, porque essencialmente o Scala no .Net ignorava o formato de reificação incompatível do C #. Se tivéssemos reificado genéricos na JVM, provavelmente esses genéricos reificados não seriam adequados para os recursos que realmente gostamos no Scala e estaríamos presos a algo abaixo do ideal. Citando o blog de Ola Bini ,
Pessoalmente, não considero a necessidade de usar um
TypeTag
contexto vinculado no Scala para métodos conflitantes sobrecarregados como uma desvantagem, porque move a sobrecarga e a inflexibilidade da reificação de um global (programa inteiro e todos os idiomas possíveis) para um site de uso por idioma problema que é apenas o caso com menos frequência.fonte