Domanda

Come posso scoprire cosa ha causato a equals () di restituire false?

Non sto chiedendo un approccio sicuro, sempre giusto, ma qualcosa che aiuti nel processo di sviluppo. Attualmente devo entrare nelle chiamate equals () (di solito un albero di loro) fino a quando uno di loro è falso, quindi entrare in esso, fino alla nausea.

Ho pensato di usare il grafico a oggetti, di inviarlo a XML e di confrontare i due oggetti. Tuttavia, XMLEncoder richiede costruttori predefiniti, jibx richiede pre-compilation, x-stream e api semplici non sono utilizzati nel mio progetto. Non mi dispiace copiare una singola classe, o anche un pacchetto, nella mia area di test e usarlo lì, ma importare un intero vaso per questo non accadrà.

Ho anche pensato di costruire da solo un attraversatore di grafici a oggetti e potrei ancora farlo, ma odio iniziare a trattare casi speciali (raccolte ordinate, raccolte non ordinate, mappe ...)

Qualche idea su come procedere?

Modifica: so che aggiungere vasi è il modo normale di fare le cose. So che i vasetti sono unità riutilizzabili. Tuttavia, la burocrazia necessaria (nel mio progetto) per questo non giustifica i risultati: continuerei a eseguire il debug e ad intervenire.

È stato utile?

Soluzione

Presumibilmente non è un confronto grafico completo ... a meno che i tuoi uguali includano tutte le proprietà in ogni classe ... (potresti provare == :))

Prova hamcrest matchers - puoi comporre ogni matcher in un " tutto di " 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;

Dirà cose come: "prevede che myField1 abbia un valore di" valore " ma era " un valore diverso " "

Naturalmente puoi incorporare le fabbriche statiche. Questo è un po 'più pesante dell'utilizzo di apache-commons EqualsBuilder , ma ti dà una descrizione accurata di cosa è fallito.

Puoi creare il tuo matcher specializzato per creare rapidamente queste espressioni. Sarebbe saggio copiare apache-commons EqualsBuilder qui.

A proposito, il barattolo di base più amaro è 32K (compresa la fonte!) dandoti la possibilità di rivedere il codice e dire ai tuoi capi "lo sostengo come il mio codice". (che presumo sia il tuo problema di importazione).

Altri suggerimenti

Puoi utilizzare gli aspetti per applicare la patch "uguale" a " sulle classi nel grafico degli oggetti e farli registrare lo stato dell'oggetto in un file quando restituiscono false. Per registrare lo stato dell'oggetto è possibile utilizzare qualcosa come beanutils per ispezionare l'oggetto e scaricarlo. Questa è una soluzione basata su jar che può essere facilmente utilizzata nell'area di lavoro

Se la gerarchia degli oggetti archiviati nel tuo albero è abbastanza semplice, puoi posizionare punti di interruzione condizionali nelle tue classi "uguale". implementazione che si innesca solo quando "uguale" restituisce false che limiterebbe il numero di volte in cui devi intervenire ... puoi usarlo ovunque tu abbia accesso al debugger. eclipse lo gestisce bene.

Sembra che tu voglia java-diff , o qualcosa del genere esso.

Okay, questo è un modo completamente bizzarro di vederlo, ma che ne dici di introdurre un nuovo metodo statico:

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 so, l'ultimo bit sembra pazzo ... ma la differenza è che puoi mettere un breakpoint sul "return false". Se poi usi breakableEquals in tutti i tuoi confronti di uguaglianza profonda, puoi interrompere non appena premi il primo " return false " ;.

Questo non aiuta molto se si stanno confrontando molti valori primitivi, certo ... ma potrebbe essere utile. Non posso dire di averlo mai usato, ma non riesco a capire perché non funzioni. Sarà un po 'meno efficiente, ovviamente - quindi se hai a che fare con codice ad alte prestazioni potresti volerlo cambiare in seguito.

Un'altra opzione sarebbe quella di usare qualcosa come:

boolean result = // comparison;
return result;

Supponendo che il tuo IDE li supporti, puoi quindi inserire un breakpoint condizionale sull'istruzione return e impostare la condizione su " ! result " ;.

Ancora un'altra opzione:

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

È quindi possibile utilizzare questo nei confronti:

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

Spero che quando non esegui il debug, questo sia ottimizzato da JIT - ma, di nuovo, puoi usare un breakpoint condizionale in noOp . Rende il codice più brutto, sfortunatamente.

In breve: nessuna soluzione particolarmente interessante qui, ma solo alcune idee che potrebbe aiutare in determinate situazioni.

  

Non mi dispiace copiare una singola classe,   o anche un pacchetto, nella mia area di test   e usandolo lì, ma importando a   l'intero vaso per questo non lo farà   accadere.

Um ... cosa? L'aggiunta di un barattolo al tuo percorso di classe è, se non altro, più semplice e meno inquietante per il progetto rispetto alla copia di classi o interi pacchetti come codice sorgente.

Per quanto riguarda il tuo problema specifico, hai molte classi diverse che usano molte proprietà diverse per determinare l'uguaglianza o hai solo un oggetto grafico profondamente annidato essenzialmente delle stesse classi? In quest'ultimo caso, sarebbe molto semplice strutturare semplicemente i metodi equals () in modo da poter mettere dei punti di interruzione su " return false " dichiarazioni. Nel primo caso, questo potrebbe essere troppo lavoro, suppongo. Tuttavia, un confronto basato su XML potrebbe non funzionare neanche, poiché mostrerà differenze tra oggetti semanticamente uguali (ad esempio set e mappe).

Dato che il tuo progetto non è in grado di aggiungere un jar, sembra al di là di una risposta SO fornire un'intera implementazione di una soluzione che altri progetti richiedono una notevole quantità di codice per realizzare (e includere bene in un Jar per te) .

Che ne dici di una soluzione non di codice - breakpoint condizionali nel debugger? È possibile aggiungere punti di interruzione che inciampano solo se il metodo restituisce false e inserirli in tutte le classi pertinenti. Nessun passo.

  

So che aggiungere vasi è il modo normale di fare le cose. So che i vasetti sono unità riutilizzabili. Tuttavia, la burocrazia necessaria (nel mio progetto) per questo non giustifica i risultati: continuerei a eseguire il debug e ad intervenire.

Un modo per aggirare questo è quello di includere una libreria come Spring (che attira un sacco di altri jar). Ho visto il progetto Spring che in realtà non utilizzava alcun vaso Spring, in modo che potessero usare qualsiasi vaso in bundle con esso.

commons-jxpath potrebbe essere utile per esaminare rapidamente l'albero degli oggetti. Non capisco appieno il problema dell'inclusione dei vasetti, ma puoi semplicemente usarlo nel tuo progetto in qualunque IDE usi che presumibilmente ti consente di usare le espressioni durante il debug.

Forse questo articolo sulla traccia del metodo ti aiuterà.

  

Come funziona

     

Un caricatore di classi personalizzato legge il file di classe e strumenti ogni metodo con codice di traccia. Il caricatore di classi aggiunge inoltre un campo statico a ciascuna classe. Questo campo ha due stati, "on" e "off". Il codice di traccia controlla questo campo prima della stampa. Le opzioni della riga di comando accedono e modificano questo campo statico per controllare l'output di traccia.

L'output di esempio che mostrano sembra promettente per il tuo problema, in quanto mostra il valore di ritorno di alcuni metodi (come isApplet):

  

isApplet = false

Dovrebbe essere facile individuare la classe esatta che ha iniziato a restituire false in uguale. Ecco l'output di esempio completo dalla pagina:

  

% java -jar /home/mike/java/trace.jar -classpath " /home/mike/jdk1.3/demo/jfc/SwingSet2/SwingSet2.jar" -escludere 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)
  |||||getResourceBundle=java.util.PropertyResourceBundle@6989e
  |||| getString = " Swing barra dei menu demo "

XMLEncoder codifica solo le proprietà dei bean, mentre uguali possono, ovviamente, funzionare su non-bean e su qualsiasi campo interno.

Parte del problema è che non sai cosa sta effettivamente guardando. Un oggetto potrebbe avere molti campi diversi e ancora affermare che era uguale a qualche altro oggetto, potrebbe anche essere un tipo diverso. (ad esempio, una classe URL personalizzata potrebbe restituire true per una stringa uguale alla sua forma esterna).

Quindi non penso che sia possibile senza strumentazione bytecode dove puoi effettivamente modificare la funzione di classi equals () per vedere a quali campi accede. Anche in quel caso, sarebbe ancora estremamente difficile sapere "veramente" perché una funzione è risultata falsa. Ma si spera che sarebbe un semplice problema di confrontare i campi a cui si accede effettivamente in uguali ()

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top