Question

Comment puis-je savoir ce qui a causé la valeur false à equals ()?

Je ne parle pas d'une approche sûre, toujours correcte, mais de quelque chose d'aide au processus de développement. Actuellement, je dois entrer dans les appels d'equals () (généralement une arborescence) jusqu'à ce que l'un d'eux soit faux, puis y aller, ad nauseam.

J'ai pensé utiliser le graphe d'objet, le sortir en XML et comparer les deux objets. Cependant, XMLEncoder nécessite des constructeurs par défaut, jibx nécessite une pré-compilation, x-stream et des API simples ne sont pas utilisés dans mon projet. Cela ne me dérange pas de copier une seule classe, ou même un paquet, dans ma zone de test et de l’utiliser là-bas, mais importer un fichier entier pour cela ne se produira tout simplement pas.

Je pensais aussi à la construction d'un traverseur de graphe d'objet moi-même, et je le ferais peut-être, mais je détesterais commencer à traiter des cas particuliers (collections ordonnées, collections non ordonnées, cartes ...)

Avez-vous une idée de comment s'y prendre?

Edit: Je sais que l’ajout de bocaux est la façon habituelle de faire les choses. Je sais que les pots sont des unités réutilisables. Cependant, la bureaucratie nécessaire (à mon projet) pour cela ne justifie pas les résultats - je continuerais à déboguer et à entrer.

Était-ce utile?

La solution

Ce n'est probablement pas une comparaison graphique complète ... sauf si vos équivalents incluent toutes les propriétés de chaque classe ... (vous pouvez essayer ==:))

Essayez appaireurs hamcrest : vous pouvez composer chaque élément dans un " ensemble de " matcher:

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;

Il indiquera des choses comme: "prévu que myField1 ait une valeur de" valeur ". mais était "une valeur différente" '

Bien sûr, vous pouvez intégrer les usines statiques. C'est un peu plus lourd que d'utiliser apache-commons EqualsBuilder , mais il vous donne une description précise de ce qui a échoué.

Vous pouvez créer votre propre matcher spécialisé pour créer rapidement ces expressions. Il serait sage de copier apache-commons EqualsBuilder ici.

BTW, le pot de base de hamcrest est de 32K (y compris la source!), ce qui vous permet d’examiner le code et de dire à vos patrons "Je maintiendrai ceci comme mon propre code". (ce qui, je suppose, est votre problème d’importation).

Autres conseils

Vous pouvez utiliser des aspects pour appliquer un correctif à "égal". sur les classes de votre graphe d'objets et leur faire consigner l'état de l'objet dans un fichier lorsqu'ils renvoient false. Pour consigner l'état de l'objet, vous pouvez utiliser quelque chose comme beanutils pour inspecter l'objet et le vider. Ceci est une solution basée sur un bocal qui peut être facilement utilisée dans votre espace de travail

Si la hiérarchie des objets stockés dans votre arborescence est assez simple, vous pouvez placer des points d'arrêt conditionnels sur vos classes "égal". une implémentation qui ne se déclenche que lorsque " égal " Retourne false, ce qui limiterait le nombre de fois où vous devez entrer ... vous pouvez l'utiliser partout où vous avez accès au débogueur. Eclipse gère cela très bien.

Il semble que vous souhaitiez java-diff , ou quelque chose du genre il.

D'accord, c'est une façon complètement bizarre de voir les choses, mais pourquoi ne pas introduire une nouvelle méthode statique:

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;
    }
}

Je sais, le dernier bit a l'air fou ... mais la différence est que vous pouvez placer un point d'arrêt sur le "return false". Si vous utilisez ensuite breakableEquals dans toutes vos comparaisons d'égalité profonde, vous pouvez effectuer une pause dès que vous atteignez le premier " return false ".

Cela n'aidera pas beaucoup si vous comparez beaucoup de valeurs primitives, certes ... mais cela pourrait être utile. Je ne peux pas dire que j'ai déjà utilisé cela, mais je ne vois pas pourquoi cela ne fonctionnerait pas. Ce sera un peu moins efficace, bien sûr. Par conséquent, si vous utilisez du code haute performance, vous voudrez peut-être le modifier par la suite.

Une autre option consisterait à utiliser quelque chose comme:

boolean result = // comparison;
return result;

En supposant que votre IDE les prenne en charge, vous pouvez ensuite placer un point d'arrêt conditionnel sur l'instruction return et définir la condition sur "! result ".

Encore une autre option:

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

Vous pouvez ensuite l'utiliser dans les comparaisons:

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

J'espère que lorsque vous n'êtes pas en train de déboguer, cela sera optimisé par le JIT, mais là encore, vous pouvez utiliser un point d'arrêt conditionnel dans noOp . Malheureusement, cela rend le code plus laid.

En bref: pas de solutions particulièrement attrayantes ici, mais juste quelques idées qui pourraient aider dans certaines situations.

  

Cela ne me dérange pas de copier une seule classe,   ou même un paquet, dans ma zone de test   et l'utiliser là-bas, mais en important un   pot entier pour cela ne va tout simplement pas   arriver.

Euh ... quoi? Ajouter un fichier jar à votre chemin de classe est, au contraire, plus facile et moins gênant pour le projet que de copier des classes ou des packages entiers en tant que code source.

En ce qui concerne votre problème spécifique, avez-vous beaucoup de classes différentes qui utilisent beaucoup de propriétés différentes pour déterminer l’égalité, ou avez-vous simplement un graphe d’objets profondément imbriqué composé essentiellement des mêmes classes? Dans ce dernier cas, il serait très facile de simplement structurer les méthodes equals () afin que vous puissiez placer des points d'arrêt sur l'option "return false". déclarations. Dans le premier cas, cela pourrait être trop de travail, je suppose. Cependant, une comparaison basée sur XML peut également ne pas fonctionner, car elle montrera les différences entre les objets sémantiquement égaux (par exemple, Ensembles et Mappes).

Étant donné que votre projet ne peut pas ajouter de fichier jar, il semble bien au-delà d'une réponse SO de donner une implémentation complète d'une solution que d'autres projets nécessitent une quantité de code importante à accomplir (et à inclure dans un fichier JAR pour vous). .

Pourquoi pas une solution sans code - des points d'arrêt conditionnels dans le débogueur? Vous pouvez ajouter des points d'arrêt qui se déclenchent uniquement si la méthode renvoie false et les placer dans toutes les classes pertinentes. Pas de progression.

  

Je sais que l'ajout de bocaux est la façon habituelle de faire les choses. Je sais que les pots sont des unités réutilisables. Cependant, la bureaucratie nécessaire (à mon projet) pour cela ne justifie pas les résultats - je continuerais à déboguer et à entrer.

Une solution consiste à inclure une bibliothèque telle que Spring (qui contient des tas d’autres bocaux). J’ai vu un projet Spring ne pas utiliser de bocal Spring afin de pouvoir utiliser les bocaux fournis.

commons-jxpath pourrait être utile pour examiner rapidement l'arborescence des objets. Je ne comprends pas tout à fait la question de l'inclusion de fichiers jars, mais vous pouvez simplement l'utiliser dans votre propre projet, quel que soit l'EDI utilisé qui vous permet vraisemblablement d'utiliser des expressions lors du débogage.

Peut-être que cet article sur le traçage des méthodes vous aidera.

  

Comment ça marche

     

Un chargeur de classe personnalisé lit le fichier de classe et instruit chaque méthode avec du code de traçage. Le chargeur de classes ajoute également un champ statique à chaque classe. Ce champ a deux états, "activé" et "désactivé". Le code de traçage vérifie ce champ avant l'impression. Les options de ligne de commande permettent d'accéder à ce champ statique et de le modifier afin de contrôler la sortie de traçage.

L'exemple de sortie qu'ils montrent semble prometteur pour votre problème, car il indique la valeur de retour de certaines méthodes (comme isApplet):

  

isApplet = false

Il devrait être facile pour vous de repérer la classe exacte qui a commencé à renvoyer faux en égaux. Voici l'exemple de sortie complet de la page:

  

% java -jar /home/mike/java/trace.jar -classpath "/home/mike/jdk1.3/demo/jfc/SwingSet2/SwingSet2.jar" -exclude 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 = false
  ..showSplashScreen
  .run
  || initializeDemo (SwingSet2 @ 3d12a6)
  ||| createMenus (SwingSet2 @ 3d12a6)
  |||| getString (SwingSet2 @ 3d12a6, "MenuBar.accessible_description")

  ||||| getResourceBundle (SwingSet2 @ 3d12a6)
  |||||getgetesourceBundle=java.util.PropertyResourceBundle@6989e
  |||| getString = " Barre de menu de démonstration Swing "

XMLEncoder ne code que les propriétés du bean, alors qu'égal à peut évidemment fonctionner sur des non-beans et sur des champs internes.

Une partie du problème réside dans le fait que vous ne savez pas ce que égal est en train de regarder. Un objet peut avoir plusieurs champs différents et prétendre qu'il est égal à un autre objet, il peut même s'agir d'un type différent. (par exemple, une classe d'URL personnalisée peut renvoyer true pour une chaîne équivalente à son formulaire externe).

Je ne pense donc pas que ce soit possible sans instrumentation bytecode où vous pouvez réellement modifier la fonction classes equals () pour voir les champs auxquels elle accède. Même dans ce cas, il serait toujours extrêmement difficile de "vraiment" savoir pourquoi une fonction est retournée fausse. Mais heureusement, ce serait un problème simple de comparer les champs auxquels on accède réellement avec equals ()

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top