Java é igual a (): para refletir ou não para refletir
-
02-07-2019 - |
Pergunta
Esta questão é especificamente relacionado com substituindo as método equals () para objetos com um grande número de campos. Primeiro, deixe-me dizer que este objeto grande não pode ser dividido em vários componentes sem violar princípios OO, então me dizendo "nenhuma classe deve ter mais do que os campos x" não vai ajudar.
Seguindo em frente, o problema tornou-se realidade quando eu esqueci de verificar um dos campos para a igualdade. Portanto, o meu método equals estava incorreta. Então eu pensei usar a reflexão:
--code removed because it was too distracting--
O objetivo deste post não é necessariamente para refatorar o código (este não é ainda o código que estou usando), mas em vez para obter a entrada sobre se este é ou não uma boa idéia.
Pros:
- Se um novo campo é adicionado, ele é automaticamente incluído
- O método é muito mais concisa do que 30 if
Contras:
- Se um novo campo é adicionado, ele é automaticamente incluído, por vezes, isso é indesejável
- Performance: Isso tem que ser mais lento, eu não sinto a necessidade de romper com um profiler
- whitelisting certos campos de ignorar na comparação é um pouco feio
Qualquer pensamento?
Solução
Se você queria whitelist por motivos de desempenho, considere usar uma anotação para indicar quais os campos para comparar. Além disso, esta aplicação não vai funcionar se os seus campos não tem boas implementações para equals()
.
P.S. Se você percorrer esse caminho para equals()
, não se esqueça de fazer algo semelhante para hashCode()
.
P.P.S. Eu confio em você já considerado HashCodeBuilder e EqualsBuilder .
Outras dicas
Use Eclipse, FFS!
Excluir o hashCode e igual métodos que você tem.
Botão direito do mouse sobre o arquivo.
Selecionar Source-> Gerar hashCode e igual ...
Feito! Não mais se preocupa com reflexão.
Repita para cada campo adicionado, basta usar a visualização de tópicos para eliminar seus dois métodos, e depois deixar Eclipse gerar automaticamente-los.
Se você vai fazer a abordagem reflexão, EqualsBuilder ainda é seu amigo:
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
Aqui está um pensamento, se você está preocupado com:
1 / Forgetting atualizar seus grandes série de instruções if para verificar a igualdade quando você adicionar / remover um campo.
2 / O desempenho de fazer isso no método equals ().
Tente o seguinte:
a Revert para trás / para usando a longa sequência de caso-declarações em seus equals () método.
b / ter uma única função que contém uma lista de campos (em uma matriz String) e que vai verificar que lista contra realidade (isto é, os campos reflectidos). Ele vai lançar uma exceção se eles não corresponderem.
c / Em seu construtor para este objeto, têm uma sincronizada execução única chamada para esta função (semelhante a um padrão Singleton). Em outras palavras, se este é o primeiro objecto construído por esta classe, chamada de função de verificação descrito em (b) acima.
A exceção irá torná-lo imediatamente óbvio quando você executar seu programa se você não tiver atualizado o seu de uma instrução if para combinar os campos refletidas; então você corrigir os se-afirmações e actualizar a lista de campo a partir de (b) acima.
construção subsequente de objetos não vai fazer essa verificação e seus iguais () método irá funcionar em sua velocidade máxima possível.
Por mais que tentasse, eu não tenho sido capaz de encontrar quaisquer problemas reais com essa abordagem (mentes maiores podem existir em StackOverflow) - há uma verificação de condição extra em cada construção de objeto para a execução única comportamento, mas que parece bastante menor.
Se você tentar duro o suficiente, você poderia ainda ter o seu se-declarações fora de sintonia com o seu campo de lista e campos refletidas mas a exceção irá garantir a sua lista de campos coincide com os campos refletidos e você apenas certifique-se de atualizar o se- declarações e lista de campos ao mesmo tempo.
Você sempre pode anotar os campos que fazer / não quer em seu método equals, que deve ser um simples e simples mudança para ele.
O desempenho é, obviamente, relacionado à forma como muitas vezes o objeto é realmente comparação, mas um monte de estruturas de usar mapas hash, para que seus iguais podem estar sendo usados ??mais do que você pensa.
Além disso, falando de mapas hash, você tem o mesmo problema com o método hashCode.
Finalmente, você realmente precisa para comparar todos os campos para a igualdade?
Você tem alguns bugs em seu código.
- Você não pode assumir que
this
eobj
são da mesma classe. Na verdade, é explicitamente permitido paraobj
ser qualquer outra classe. Você poderia começar comif ( ! obj instanceof myClass ) return false;
porém este é ainda não correta ??em>, porqueobj
poderia ser uma subclasse dethis
com campos adicionais que podem importa. - Você tem que suportar valores
null
paraobj
com um simplesif ( obj == null ) return false;
- Você não pode tratar
null
e string vazia como iguais. Em veznull
deleite especial. maneira mais simples aqui é começar comparandoField.get(obj) == Field.get(this)
. Se eles são ambos iguais ou ambos acontecer para apontar para o mesmo objeto, este é rápido. (Nota:. Esta é também uma otimização, o que você precisa uma vez que esta é uma rotina lenta) Se isto falhar, você pode usar oif ( Field.get(obj) == null || Field.get(this) == null ) return false;
rápido para lidar com casos onde exatamente énull
. Finalmente, você pode usar oequals()
habitual. - Você não está usando
foundMismatch
Eu concordo com Hank que [HashCodeBuilder][1]
e [EqualsBuilder][2]
é a melhor maneira de ir. É fácil de manter, não um monte de código clichê, e você evita todas estas questões.
Você pode usar anotações para excluir campos do cheque
por exemplo.
@IgnoreEquals
String fieldThatShouldNotBeCompared;
E então é claro que você verificar a presença da anotação em seu método equals genérico.
Se você tem acesso aos nomes dos campos, por que não torná-lo um padrão que campos que você não deseja incluir sempre começar com "local" ou "nochk" ou algo parecido.
Em seguida, você lista negra todos os campos que começam com isto (código não é tão feio então).
Eu não tenho dúvida de que é um pouco mais lento. Você precisa decidir se você quiser trocar a facilidade de atualizações contra a velocidade de execução.
Dê uma olhada org.apache.commons.EqualsBuilder: