Já vi várias vezes neste site postagens que desacreditam a implementação de genéricos em Java. Agora, posso dizer honestamente que não tive problemas com o uso deles. No entanto, eu não tentei fazer uma classe genérica sozinho. Então, quais são seus problemas com o suporte genérico do Java?
49
Respostas:
A implementação genérica do Java usa apagamento de tipo . Isso significa que suas coleções genéricas fortemente tipadas são realmente do tipo
Object
em tempo de execução. Isso tem algumas considerações de desempenho, pois significa que os tipos primitivos devem ser colocados em caixas quando adicionados a uma coleção genérica. É claro que os benefícios da correção do tipo em tempo de compilação superam a tolice geral do apagamento do tipo e o foco obsessivo na compatibilidade com versões anteriores.fonte
TypeLiteral
google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/…new ArrayList<String>.getClass() == new ArrayList<Integer>.getClass()
template<T> T add(T v1, T v2) { return v1->add(v2); }
e aí você tem uma maneira verdadeiramente genérica de criar uma função queadd
é duas coisas, não importa quais sejam essas coisas, elas apenas precisam ter um método nomeadoadd()
com um parâmetro.Add
exemplo simples que eu dei, não há como saber de antemão quais classes podem ser aplicadas, o que é uma desvantagem.Observe que as respostas que já foram fornecidas se concentram na combinação de Java-the-language, JVM e Java Class Library.
Não há nada de errado com os genéricos de Java no que diz respeito à linguagem Java. Conforme descrito em C # vs Java genéricos , os genéricos de Java são muito bons no nível 1 da linguagem .
O que é subótimo é que a JVM não suporta diretamente genéricos, o que tem várias consequências importantes:
Suponho que a distinção que estou fazendo seja pedante, pois o Java-the-language é onipresentemente compilado com a JVM como destino e a Java Class Library como a biblioteca principal.
1 Com a possível exceção dos curingas, que se acredita tornar a inferência de tipo indecidível no caso geral. Essa é uma grande diferença entre os genéricos de C # e Java que não é mencionada com muita frequência. Obrigado, antimônio.
fonte
8675309
) e a partir dele criar um tipo exclusivo para esse número (por exemploZ8<Z6<Z7<Z5<Z3<Z0<Z9>>>>>>>
), que teria membros diferentes de qualquer outro tipo. Em C ++, todos os tipos que poderiam ser gerados a partir de qualquer entrada teriam que ser produzidos em tempo de compilação.A crítica usual é a falta de reificação. Isto é, objetos em tempo de execução não contêm informações sobre seus argumentos genéricos (embora as informações ainda estejam presentes em campos, métodos, construtores e classes e interfaces estendidas). Isso significa que você pode lançar, digamos,
ArrayList<String>
paraList<File>
. O compilador fornecerá avisos, mas também alertará se vocêArrayList<String>
atribuir umaObject
referência a uma referência e depois convertê-la emList<String>
. As vantagens são que, com os genéricos, você provavelmente não deveria fazer a transmissão, o desempenho é melhor sem os dados desnecessários e, é claro, a compatibilidade com versões anteriores.Algumas pessoas reclamam que você não pode sobrecarregar com base em argumentos genéricos (
void fn(Set<String>)
evoid fn(Set<File>)
). Você precisa usar nomes de métodos melhores. Observe que essa sobrecarga não exigiria reificação, pois a sobrecarga é um problema estático em tempo de compilação.Tipos primitivos não funcionam com genéricos.
Curingas e limites são bastante complicados. Eles são muito úteis. Se Java preferisse interfaces de imutabilidade e de dizer-não-perguntar, os genéricos do lado da declaração e não do lado do uso seriam mais apropriados.
fonte
Object cs = new char[] { 'H', 'i' }; System.out.println(cs);
um absurdo impresso. Mude o tipo decs
parachar[]
e você receberáHi
.Os genéricos Java são ruins porque você não pode fazer o seguinte:
Você não precisaria abater todas as classes no código acima para fazê-lo funcionar como deveria se os genéricos fossem realmente parâmetros de classe genéricos.
fonte
Parser
precisarei tornar o código da fábrica ainda mais complicado. Enquanto se "genérico" realmente significava genérico, não haveria necessidade de fábricas e todas as outras bobagens que se chamam padrões de design. É uma das muitas razões pelas quais prefiro trabalhar em linguagens dinâmicas.avisos do compilador para a falta de parâmetros genéricos que não servem para nada tornam a linguagem sem sentido, por exemplo
public String getName(Class<?> klazz){ return klazz.getName();}
Os genéricos não funcionam bem com matrizes
As informações de tipo perdido tornam a reflexão uma bagunça de fundição e fita adesiva.
fonte
HashMap
vez deHashMap<String, String>
.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:
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 mesmoArrayList<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.
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.
fonte
Eu poderia dizer algumas coisas boas sobre genéricos, mas essa não era a questão. Eu poderia reclamar que eles não estão disponíveis em tempo de execução e não funcionam com matrizes, mas isso foi mencionado.
Um grande aborrecimento psicológico: ocasionalmente, encontro situações em que os genéricos não podem funcionar. (Matrizes são o exemplo mais simples.) E não consigo descobrir se os genéricos não podem fazer o trabalho ou se sou apenas estúpido. Eu odeio isso. Algo como genéricos deve funcionar sempre. Todas as vezes que não consigo fazer o que quero usando a linguagem Java, sei que o problema sou eu e sei que, se continuar pressionando, chegarei lá eventualmente. Com os genéricos, se eu me tornar persistente demais, posso perder muito tempo.
Mas o problema real é que os genéricos adicionam muita complexidade para pouco ganho. Nos casos mais simples, isso pode me impedir de adicionar uma maçã a uma lista que contém carros. Bem. Mas, sem genéricos, esse erro geraria uma ClassCastException realmente rápida no tempo de execução, com pouco tempo perdido. Se eu adicionar um carro com uma cadeira de criança com uma criança, preciso de um aviso em tempo de compilação de que a lista é apenas para carros com cadeiras de criança que contêm chimpanzés de bebê? Uma lista de instâncias simples de Objeto começa a parecer uma boa ideia.
O código gerado pode ter muitas palavras e caracteres, ocupar muito espaço extra e demorar muito mais para ler. Eu posso gastar muito tempo fazendo com que todo esse código extra funcione corretamente. Quando isso acontece, me sinto incrivelmente inteligente. Também perdi várias horas ou mais do meu próprio tempo e tenho que me perguntar se mais alguém será capaz de descobrir o código. Eu gostaria de poder entregá-lo para manutenção a pessoas que são menos inteligentes que eu ou que têm menos tempo a perder.
Por outro lado (fiquei um pouco confuso e sinto a necessidade de fornecer algum equilíbrio), é bom ao usar coleções e mapas simples para adicionar, colocar e ser verificados quando escritos, e geralmente não adiciona muito complexidade ao código ( se alguém outra pessoa escreve a coleção ou mapa) . E Java é melhor nisso que C #. Parece que nenhuma das coleções de C # que eu quero usar jamais manipula genéricos. (Admito que tenho gostos estranhos em coleções.)
fonte
Fine. But without generics this error would throw a ClassCastException really quick at run time with little time wasted.
Isso realmente depende de quanto tempo de execução ocorre entre a inicialização do programa e a entrada na linha de código em questão. Se um usuário informa o erro e ele te leva vários minutos (ou, pior cenário, horas ou mesmo dias) para reproduzir, que a verificação em tempo de compilação começa a olhar melhor e melhor ...