Há algum tempo, escrevi esta resposta para uma pergunta sobre como evitar um getter e setter para cada variável mutável. Na época, eu tinha apenas um pressentimento difícil de verbalizar de que essa é uma má ideia, mas o OP estava perguntando explicitamente como fazê-lo. Procurei aqui por que isso pode ser um problema e encontrei essa pergunta , cujas respostas parecem ter como certo que o uso da reflexão, a menos que absolutamente necessário, seja uma prática ruim.
Então, alguém pode verbalizar por que é certo que a reflexão deve ser evitada, especificamente no caso do getter e setter genérico?
java
reflection
HAEM
fonte
fonte
Map
osget
eput
, que é uma maneira bastante desajeitada de fazer as coisas.Respostas:
Desvantagens da reflexão em geral
A reflexão é mais difícil de entender do que o código linear.
Na minha experiência, a reflexão é um recurso de "nível de especialista" em Java. Eu diria que a maioria dos programadores nunca usa reflexão ativamente (ou seja, consumir bibliotecas que usam reflexão não conta). Isso torna o código mais difícil de entender para esses programadores.
Código de reflexão inacessível à análise estática
Suponha que eu tenha um getter
getFoo
na minha classe e queira renomeá-lo paragetBar
. Se eu não usar reflexão, basta pesquisar na base de códigogetFoo
e encontrar todos os locais que usam o getter para que eu possa atualizá-la, e mesmo que eu perca uma, o compilador reclamará.Mas se o local que usa o getter é algo como
callGetter("Foo")
e ocallGetter
fazgetClass().getMethod("get"+name).invoke(this)
, então o método acima não o encontrará e o compilador não irá reclamar. Somente quando o código for realmente executado, você obterá umNoSuchMethodException
. E imagine a dor que você sente se essa exceção (que é rastreada) é engolidacallGetter
porque "é usada apenas com cadeias codificadas, não pode realmente acontecer". (Ninguém faria isso, alguém poderia argumentar? Exceto que o OP fez exatamente isso em sua resposta ao SO. Se o campo fosse renomeado, os usuários do setter genérico nunca perceberiam, exceto o bug extremamente obscuro do setter silenciosamente, sem fazer nada. Os usuários do getter podem, se tiverem sorte, perceber a saída do console da exceção ignorada.)O código de reflexão não é verificado pelo tipo pelo compilador
Este é basicamente um grande sub-ponto do exposto acima. Código de reflexão é tudo
Object
. Os tipos são verificados em tempo de execução. Os erros são descobertos por testes de unidade, mas apenas se você tiver cobertura. ("É apenas um getter, não preciso testá-lo.") Basicamente, você perde a vantagem de usar o Java sobre o Python, que ganhou em primeiro lugar.O código de reflexão não está disponível para otimização
Talvez não na teoria, mas na prática, você não encontrará uma JVM que inclua ou crie um cache embutido
Method.invoke
. As chamadas de método normal estão disponíveis para essas otimizações. Isso os torna muito mais rápidos.Código de reflexão é apenas lento em geral
A pesquisa de método dinâmico e a verificação de tipo necessárias para o código de reflexão são mais lentas que as chamadas de método normais. Se você transformar esse dispositivo barato de uma linha em um animal de reflexão, poderá (eu não medi isso) observar várias ordens de magnitude de desaceleração.
Desvantagem do getter / setter genérico especificamente
Essa é apenas uma péssima idéia, porque sua classe agora não tem mais encapsulamento. Todos os campos que ele possui são acessíveis. Você também pode torná-los públicos.
fonte
Como os getter / setters de passagem são uma abominação que não fornece valor. Eles não fornecem nenhum encapsulamento, pois os usuários podem obter os valores reais por meio das propriedades. Eles são YAGNI porque você raramente muda a implementação do seu armazenamento em campo.
E porque isso tem muito menos desempenho. Em C #, pelo menos, um getter / setter irá alinhar diretamente para uma única instrução CIL. Na sua resposta, você está substituindo por três chamadas de função, que, por sua vez, precisam carregar metadados para refletir. Geralmente, usei 10x como regra geral para custos de reflexão, mas quando pesquisei dados, uma das primeiras respostas incluiu essa resposta, que aproximou 1000x.
(E torna seu código mais difícil de depurar, e prejudica a usabilidade porque há mais maneiras de usá-lo incorretamente, além de prejudicar a manutenção, porque agora você está usando seqüências de caracteres mágicas em vez de identificadores adequados ...)
fonte
getX()
esetX()
métodos que podem ser encontrados via reflexão. Os feijões não devem necessariamente encapsular seus valores, eles podem ser apenas DTOs. Portanto, em Java, os getters geralmente são uma dor necessária. As propriedades C # são muito, muito melhores em todos os aspectos.Além dos argumentos já apresentados.
Não fornece a interface esperada.
Os usuários esperam chamar foo.getBar () e foo.setBar (valor), não foo.get ("bar") e foo.set ("bar", valor)
Para fornecer a interface que os usuários esperam, você precisa escrever um getter / setter separado para cada propriedade. Depois de fazer isso, não há sentido em usar a reflexão.
fonte
Claro que não é uma má ideia, é apenas uma má ideia (em um mundo java normal) (quando você só quer um getter ou setter). Por exemplo, algumas estruturas (spring mvc) ou librares já o usam (jstl) dessa maneira, alguns analisadores json ou xml estão usando, se você deseja usar um DSL, você o usará. Então isso depende do seu cenário de caso de uso.
Nada está certo ou errado como fato.
fonte