Pregunta

¿Cómo puedo averiguar qué causó que equals () devuelva falso?

No estoy preguntando por un enfoque seguro, siempre correcto, sino por algo que ayude en el proceso de desarrollo. Actualmente tengo que intervenir en las llamadas equals () (generalmente un árbol de ellas) hasta que una de ellas sea falsa, luego intervenir, hasta la saciedad.

Pensé en usar el gráfico de objetos, enviarlo a xml y comparar los dos objetos. Sin embargo, XMLEncoder requiere constructores predeterminados, jibx requiere precompilación, x-stream y la API simple no se utilizan en mi proyecto. No me importa copiar una sola clase, o incluso un paquete, en mi área de prueba y usarla allí, pero importar un frasco completo para esto simplemente no va a suceder.

También pensé en construir un trazador de gráficos de objetos yo mismo, y todavía podría hacerlo, pero odiaría comenzar a tratar casos especiales (colecciones ordenadas, colecciones no ordenadas, mapas ...)

¿Alguna idea de cómo hacerlo?

Editar: Sé que agregar frascos es la forma normal de hacer las cosas. Sé que los frascos son unidades reutilizables. Sin embargo, la burocracia necesaria (en mi proyecto) para esto no justifica los resultados: seguiría depurando y avanzando.

¿Fue útil?

Solución

Presumiblemente no es una comparación gráfica completa ... a menos que sus iguales incluyan todas las propiedades en cada clase ... (podría intentar == :))

Pruebe hamcrest matchers : puede componer cada matcher en un " todos " comparador:

Matcher<MyClass> matcher = CoreMatchers.allOf(
  HasPropertyWithValue.hasProperty("myField1", getMyField1()),
  HasPropertyWithValue.hasProperty("myField2", getMyField2()));
if (!matcher.matches(obj)){
  System.out.println(matcher.describeFailure(obj));
  return false;
}
return true;

Dirá cosas como: 'esperaba que myField1 tuviera un valor de " value " pero era "un valor diferente" "

Por supuesto, puede alinear las fábricas estáticas. Esto es un poco más pesado que usar apache-commons EqualsBuilder , pero le da una descripción precisa de exactamente lo que falló.

Puede crear su propio matizador especializado para crear rápidamente estas expresiones. Sería aconsejable copiar apache-commons EqualsBuilder aquí.

Por cierto, el tarro básico de Hamcrest es 32K (¡incluida la fuente!), lo que le da la opción de revisar el código y decirle a sus jefes "Voy a mantener esto como mi propio código". (que supongo que es su problema de importación).

Otros consejos

Podría usar aspectos para parchear "igual" en las clases en su gráfico de objetos y haga que registren el estado del objeto en un archivo cuando devuelvan falso. Para registrar el estado del objeto, puede usar algo como beanutils para inspeccionar el objeto y volcarlo. Esta es una solución basada en jar que se puede usar fácilmente en su espacio de trabajo

Si la jerarquía de los objetos almacenados en su árbol es lo suficientemente simple, podría colocar puntos de interrupción condicionales en sus clases '' igual '' implementación que solo se activa cuando " igual " devuelve falso, lo que limitaría la cantidad de veces que tiene que intervenir ... puede usar esto donde tenga acceso al depurador. eclipse maneja esto muy bien.

Parece que quieres java-diff , o algo así

Bien, esta es una forma completamente extraña de verlo, pero ¿qué tal si introducimos un nuevo método estático?

public static boolean breakableEquals(Object o1, Object o2)
{
    if (o1 == o2)
    {
        return true;
    }
    if (o1 == null || o2 == null)
    {
        return false;
    }
    // Don't condense this code!
    if (o1.equals(o2))
    {
        return true;
    }
    else
    {
        return false;
    }
}

Lo sé, la última parte parece una locura ... pero la diferencia es que puedes poner un punto de interrupción en el " return false " ;. Si luego usa breakableEquals en todas sus comparaciones de igualdad profunda, entonces puede romper tan pronto como presione el primer " return false " ;.

Esto no ayuda mucho si está comparando muchos valores primitivos, sin duda ... pero podría ser útil. No puedo decir que alguna vez haya usado esto, pero no veo por qué no funcionaría. Por supuesto, será un poco menos eficiente, por lo que si se trata de un código de alto rendimiento, es posible que desee cambiarlo después.

Otra opción sería usar algo como:

boolean result = // comparison;
return result;

Suponiendo que su IDE los admita, puede poner un punto de interrupción condicional en la declaración de retorno y establecer la condición para que sea "resultado ! ".

Otra opción más:

public static boolean noOp(boolean result)
{
    return result;
}

Puede usar esto en las comparaciones:

return Helpers.noOp(x.id == y.id) &&
       Helpers.noOp(x.age == y.age);

Espero que cuando no esté depurando, esto sea optimizado por el JIT, pero nuevamente, puede usar un punto de interrupción condicional en noOp . Desafortunadamente, hace que el código sea más feo.

En resumen: no hay soluciones particularmente atractivas aquí, sino solo algunas ideas que podrían ayudar en ciertas situaciones.

  

No me importa copiar una sola clase,   o incluso un paquete, en mi área de prueba   y usarlo allí, pero importando un   toda la jarra para esto simplemente no va a   suceder.

Um ... ¿qué? Agregar un jar a su classpath es, en todo caso, más fácil y menos molesto para el proyecto que copiar clases o paquetes completos como código fuente.

En cuanto a su problema específico, ¿tiene muchas clases diferentes que usan muchas propiedades diferentes para determinar la igualdad, o simplemente tiene un gráfico de objeto profundamente anidado de esencialmente las mismas clases? En el último caso, sería muy fácil simplemente estructurar los métodos equals () para que pueda poner puntos de interrupción en el " return false " declaraciones. En el primer caso, supongo que esto podría ser demasiado trabajo. Pero entonces, una comparación basada en XML puede no funcionar tampoco, ya que mostrará diferencias entre objetos semánticamente iguales (por ejemplo, Conjuntos y Mapas).

Dado que su proyecto no puede agregar un jar, parece más allá de una respuesta SO dar una implementación completa de una solución que otros proyectos requieren una cantidad sustancial de código para lograr (e incluir muy bien en un Jar para usted) .

¿Qué tal una solución sin código - puntos de interrupción condicionales en el depurador? Puede agregar puntos de interrupción que se disparan solo si el método devuelve falso y colocarlos en todas las clases relevantes. Sin pisar.

  

Sé que agregar frascos es la forma normal de hacer las cosas. Sé que los frascos son unidades reutilizables. Sin embargo, la burocracia necesaria (en mi proyecto) para esto no justifica los resultados: seguiría depurando y avanzando.

Una forma de evitar esto es incluir una biblioteca como Spring (que tira de un montón de otro jar). He visto el proyecto Spring que en realidad no usó ningún Spring jar solo para que pudieran usar cualquier jar que esté incluido en él.

commons-jxpath podría ser útil para examinar rápidamente el árbol de objetos. No entiendo completamente el problema de incluir frascos, pero puede usarlo en su propio proyecto en cualquier IDE que use que presumiblemente le permita usar expresiones durante la depuración.

Quizás este artículo sobre el rastreo de métodos lo ayude.

  

Cómo funciona

     

Un cargador de clases personalizado lee el archivo de clase e instrumenta cada método con código de seguimiento. El cargador de clases también agrega un campo estático a cada clase. Este campo tiene dos estados, 'activado' y 'desactivado'. El código de seguimiento verifica este campo antes de la impresión. Las opciones de línea de comando acceden y modifican este campo estático para controlar la salida de rastreo.

El resultado de muestra que muestran parece prometedor para su problema, ya que muestra el valor de retorno de algunos métodos (como isApplet):

  

isApplet = false

Debería ser fácil para usted detectar la clase exacta que comenzó a devolver falso en iguales. Aquí está la salida de muestra completa de la página:

  

% java -jar /home/mike/java/trace.jar -classpath " /home/mike/jdk1.3/demo/jfc/SwingSet2/SwingSet2.jar" -excluye CodeViewer SwingSet2
  | SwingSet2. ()
  | SwingSet2.
  | SwingSet2.main ([Ljava.lang.String; @ 1dd95c)
  || isApplet (SwingSet2 @ 3d12a6)
  || isApplet = false
  || SwingSet2.createFrame (apple.awt.CGraphicsConfig@93537d)
  ||SwingSet2.createFrame=javax.swing.JFrame@cb01e3
  || createSplashScreen (SwingSet2 @ 3d12a6)
  ||| createImageIcon (SwingSet2 @ 3d12a6, " Splash.jpg " ;, " Splash.accessible_description ")
  |||createImageIcon=javax.swing.ImageIcon@393e97
  ||| isApplet (SwingSet2 @ 3d12a6)
  ||| isApplet = false
  ||| getFrame (SwingSet2 @ 3d12a6)
  |||getFrame=javax.swing.JFrame@cb01e3
  ||| getFrame (SwingSet2 @ 3d12a6)
  |||getFrame=javax.swing.JFrame@cb01e3
  || createSplashScreen
  .run (SwingSet2 $ 1 @ fba2af)
  ..showSplashScreen (SwingSet2 @ 3d12a6)
  ... isApplet (SwingSet2 @ 3d12a6)
  ... isApplet = falso
  ..showSplashScreen
  .run
  || initializeDemo (SwingSet2 @ 3d12a6)
  ||| createMenus (SwingSet2 @ 3d12a6)
  |||| getString (SwingSet2 @ 3d12a6, " MenuBar.accessible_description ")
  ||||| getResourceBundle (SwingSet2 @ 3d12a6)
  |||||getResourceBundle=java.util.PropertyResourceBundle@6989e
  |||| getString = " Barra de menú de demostración de Swing "

XMLEncoder solo codifica las propiedades del bean, mientras que igual puede, obviamente, trabajar en no beans y en cualquier campo interno.

Parte del problema es que no sabes qué es lo que realmente está mirando igual. Un objeto podría tener muchos campos diferentes y aun así afirmar que era igual a otro objeto, incluso puede ser un tipo diferente. (por ejemplo, una clase de URL personalizada puede devolver verdadero para una cadena que equivale a su forma externa).

Así que no creo que sea posible sin instrumentación de bytecode donde puedes modificar las funciones de la clase equals () para ver a qué campos accede. Incluso entonces, aún sería extremadamente difícil saber "verdaderamente" por qué una función devuelve falso. Pero con suerte sería una simple cuestión de comparar los campos a los que realmente se accede en equals ()

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top