2008-09-23 18 views
9

Esta pregunta está específicamente relacionada con anular el método equals() para objetos con una gran cantidad de campos. En primer lugar, permítanme decir que este gran objeto no se puede dividir en varios componentes sin violar los principios de OO, por lo que decirme "ninguna clase debe tener más de x campos" no ayudará.Java es igual a(): para reflejar o no para reflejar

Avanzando, el problema se concretó cuando olvidé comprobar uno de los campos por igualdad. Por lo tanto, mi método equals fue incorrecto. Entonces pensé en utilizar la reflexión:

--code removed because it was too distracting-- 

El objetivo de este post no es necesariamente refactorizar el código (esto no es ni siquiera el código que estoy utilizando), pero en lugar de obtener el aporte de si es o no es una buena idea.

Pros:

  • Si se añade un nuevo campo, se incluye automáticamente
  • El método es mucho más concisa de 30 sentencias if

Contras:

  • Si se añade un nuevo campo, que se incluye de forma automática, a veces esto no es deseable
  • Rendimiento: Esto tiene que ser más lento, no me siento la necesidad de romper con un perfilador
  • listas blancas ciertos campos de ignorar en el la comparación es un poco fea

¿Alguna idea?

Respuesta

13

Si desea incluir en la lista blanca por motivos de rendimiento, considere usar una anotación para indicar qué campos comparar. Además, esta implementación no funcionará si sus campos no tienen buenas implementaciones para equals().

P.S. Si usa esta ruta para equals(), no olvide hacer algo similar para hashCode().

P.P.S. Confío en que ya ha considerado HashCodeBuilder y EqualsBuilder.

+0

Todos los campos son cadenas o primitivas @ P.S. ¡Ya lo hice, pero no está de más recordarlo a todos! @ P.P.S. Sabía que tenía que haber algo allí afuera. Había estado usando ToStringBuilder, no tengo idea de cómo los pasé por alto. La verdadera pregunta es reflejar o no reflexionar. – oreoshake

+0

Depende de lo que piense que es más probable: agregar un campo y olvidarse de agregarlo a equals() o agregar un campo que no quiere y/o no puede usar en la comparación automática, aunque usar una anotación podría ayudar . Puede considerar algo como flexjson y comparar la salida de cada objeto. –

+0

Esos constructores son geniales. –

1

Siempre puede anotar los campos que desea/no desea en su método equals, que debería ser un cambio directo y sencillo.

El rendimiento está obviamente relacionado con la frecuencia con la que se compara realmente el objeto, pero muchos marcos utilizan mapas hash, por lo que puede que sus iguales estén siendo utilizados más de lo que cree.

Además, hablando de mapas hash, tiene el mismo problema con el método hashCode.

Finalmente, ¿realmente necesita comparar todos los campos para la igualdad?

+0

De hecho, me equivoqué, tenemos un método hashcode/equals que solo usa el ID del registro (desde un db) y dado que esto siempre será único, el método hashCode/equals solo compara los ID. Pero necesitamos verificar si los objetos son equivalentes, teniendo todos los campos iguales. – oreoshake

0

Si tiene acceso a los nombres de los campos, ¿por qué no establece como estándar que los campos que no desea incluir siempre comiencen con "local" o "nochk" o algo por el estilo?

Luego lista negra todos los campos que comienzan con esto (el código no es tan feo entonces).

No lo dudo, es un poco más lento. Debe decidir si desea intercambiar la facilidad de actualización con la velocidad de ejecución.

+1

Creo que agregar convenciones para algo como esto reduce la legibilidad innecesariamente. – oreoshake

1

Tiene algunos errores en su código.

  1. No se puede asumir que this y obj son de la misma clase. De hecho, está explícitamente permitido que obj sea de cualquier otra clase. Puede comenzar con if (! obj instanceof myClass) return false; sin embargo, esto es aún no es correcto porque obj podría ser una subclase de this con campos adicionales que pueden ser importantes.
  2. Tienes que apoyar null valores para obj con un simple if (obj == null) return false;
  3. No se puede tratar y null cadena vacía como iguales. En su lugar, trate null especialmente. La manera más simple aquí es comenzar comparando Field.get(obj) == Field.get(this). Si ambos son iguales o ambos apuntan al mismo objeto, esto es rápido. (Nota: Esta también es una optimización, que necesita ya que esta es una rutina lenta.) Si esto falla, puede usar el if (Field.get(obj) == null || Field.get(this) == null) return false; rápido para manejar casos donde exactamente uno es null. Finalmente puede usar el equals() habitual.
  4. no se está usando foundMismatch

Estoy de acuerdo con Hank que [HashCodeBuilder][1] y [EqualsBuilder][2] es un mejor camino a seguir. Es fácil de mantener, no muchos códigos repetitivos, y evita todos estos problemas.

2

Aquí es un pensamiento si usted está preocupado acerca de:

1/olvido de actualizar su gran serie de si- declaraciones para verificar la igualdad cuando agrega/elimina un campo.

2/El rendimiento de hacer esto en el método equals().

intente lo siguiente:

a/volver a utilizar el método de larga secuencia de sentencias if en sus iguales().

b/Tiene una sola función que contiene una lista de los campos (en una matriz de Cadenas) y que verificará esa lista contra la realidad (es decir, los campos reflejados). Lanzará una excepción si no coinciden.

c/En su constructor para este objeto, tenga una llamada de ejecución sincronizada a esta función (similar a un patrón singleton). En otras palabras, si este es el primer objeto construido por esta clase, llame a la función de verificación descrita en (b) arriba.

La excepción lo hará inmediatamente obvio cuando ejecute su programa si no ha actualizado sus declaraciones if para que coincidan con los campos reflejados; luego corrige los enunciados if y actualiza la lista de campos de (b) anterior.

La construcción posterior de objetos no hará esta comprobación y su método equals() se ejecutará a su velocidad máxima posible.

mucho que lo intentaba, no he podido encontrar ningún problema real con este enfoque (mayores mentes pueden existir en StackOverflow) - hay una condición de comprobación extra en cada construcción de objetos para el comportamiento de una sola ejecución, pero que parece bastante menor

Si lo intenta lo suficiente, aún podría obtener sus declaraciones if fuera de paso con su lista de campos y campos reflejados, pero la excepción garantizará que su lista de campos coincida con los campos reflejados y solo se asegura de actualizar el -estados y lista de campos al mismo tiempo.

5

Si tienes que ir el enfoque reflexión, EqualsBuilder sigue siendo su amigo:

public boolean equals(Object obj) { 
    return EqualsBuilder.reflectionEquals(this, obj); 
} 
1

Usted podría utilizar anotaciones para excluir los campos del cheque

por ejemplo,

@IgnoreEquals 
String fieldThatShouldNotBeCompared; 

Y entonces por supuesto que comprueba la presencia de la anotación en su método es igual genérico.

9

¡Utilice Eclipse, FFS!

Borre el código hash e iguale a los métodos que tenga.

Haga clic derecho en el archivo.

Seleccionar Fuente-> Generar código hash y es igual ...

hecho! No más preocupaciones sobre la reflexión.

Repita para cada campo agregado, solo use la vista de esquema para eliminar sus dos métodos, y luego deje que Eclipse los autogenere.

+2

Aunque es más fácil que escribir el método a mano, esto comparte las principales desventajas tanto de los métodos reflejantes como de los codificados a mano. El método aún puede estar desactualizado cuando se agregan campos y el método no se regenera, y aún se pueden verificar los campos que no deberían estar cuando alguien regenera el método y se olvida de quitar el cheque. –

+0

A veces no hay una bala mágica. Pero la solución más simple suele ser la forma más fácil de hacerlo en estos casos. – MetroidFan2002

Cuestiones relacionadas