2012-11-22 17 views
9

È stato chiesto in precedenza, ma non ho trovato un'implementazione decente con una spiegazione.Java: metodo di confronto Null safe

public int compareTo(Object o) 
{ 
    if (this == null || o == null) 
    { 
     return 0; 
    } 
    Tok tmp = (Tok) o;  
    if (this.rang < tmp.rang) 
    { 
     return -1; 
    } else if (this.rang > tmp.rang) { 
     return 1; 
    } else { 
     return 0; 
    } 
} 

Ho letto due domande simili che ho trovato ancora; insistono per implementare un altro metodo. Non capisco perché questo non dovrebbe funzionare. Il metodo ottiene un oggetto aggiuntivo e controlla se è un'istanza valida o null, se null restituisce semplicemente 0; quale sarebbe il modo più semplice per implementare la sicurezza n. compareTo.

L'implementazione che ha funzionato per me è stato:

public int compareTo(Object o) 
{ 
    if (o == null) 
    { 
     return 0; 
    } 
    Tok tmp = (Tok) o;  
    if (this.rang < tmp.rang) 
    { 
     return -1; 
    } else if (this.rang > tmp.rang) { 
     return 1; 
    } else { 
     return 0; 
    } 
} 

non è l'attuazione ottimale si dovrebbe guardare a ciò che le persone buone pubblicati qui le risposte. Per il mio caso particolare questo è stato abbastanza decente in quanto questo non è mai nullo eppure l'oggetto ricevuto può essere nullo e l'implementazione iniziale indica se uno dei due è null return 0. Quindi se l'oggetto dato è nullo 0 viene restituito.

+12

'questo 'è ** mai **' null' in Java. –

+0

@MattBall probabilmente intendeva ** this.someInstance ** – PermGenError

+0

questo non può mai essere nullo –

risposta

12

Personalmente, mi piace il Guava's Ordering per il confronto sicuro. È possibile specificare #nullsFirst() o #nullsLast() per evitare NullPointerException s.

Altre note importanti, per lo più dai commenti:

  • this è mainull in Java
  • considerare l'utilizzo di Guava's ComparisonChain se si sta implementando un grana fine compareTo()
  • Nell'attuare Comparable , assicurati di specificare il parametro type in modo da ottenere la sicurezza del tipo in fase di compilazione e non devi utilizzare instanceof o cast:

    class Tok implements Comparable<Tok> { 
        // snip 
    
        public int compareTo(Tok other) { 
         // snip 
        } 
    } 
    
5

Restituire 0 implicherebbe che this e o sono uguali, il che non è vero se o è nullo. Inoltre, this non sarà mai nullo.

È dipendente dall'applicazione, ovviamente. Potresti voler avere un oggetto che dovrebbe essere uguale a null. Ciò che ritorni dipende da te, ma se stai cercando un metodo generale nullo, non è davvero l'ideale.

Per essere completamente generico, vorrei verificare se o è null e, in tal caso, lanciare una specie di Eccezione.

+1

Specificamente un NullPointerException secondo la documentazione di Comparable. http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html – DPM

+0

Non ho bisogno di eccezioni ho un array di oggetti Tok [] lo do a Arrays.sort() e io ottenere l'eccezione Null. –

+0

Emmerioch ha ragione, fidati di noi, non controllare Null, vedi la mia risposta qui sotto, ma rendi il tuo codice pulito, rimuovendo o evitando oggetti nulli nel token [] che vuoi ordinare! Vai in modo pulito, altrimenti passi molto tempo a cercare un bug, probabilmente non lo troverai facilmente. – AlexWien

1

Confrontando due oggetti può essere nullo sicuro come qualsiasi altro metodo, il fermo qui è che un normale metodo ha due parametri ma compareTo riceve uno e l'altro è l'oggetto stesso.

this non può MAI essere nullo, significherebbe che si sta eseguendo il codice in un oggetto null (nessuna istanza). In questo caso verrà lanciato un numero NullPointerException al richiamo di compareTo, rendendo impossibile l'esecuzione del suo codice.

Ci sono tanti approcci quanti sono gli oggetti, perché un confronto può essere basato nei campi di una classe che può essere nullo (protezioni previste per escludere i tipi primitivi). Quindi, per farla breve, i controlli Null dovrebbero coprire l'oggetto ricevuto come parametro in compareTo e i campi utilizzati. Inoltre, se si dispone di un'istanza esterna che contiene una logica (ad esempio una classe di utilità), è necessario verificare se anche tale istanza è nullo.

Come nota a margine, qualunque cosa si restituisca se qualsiasi oggetto coinvolto è null deve essere coerente e documentato (è possibile restituire -1 o 1, per inserire valori nulli all'inizio o alla fine). Basta evitare il ritorno 0 (che sarebbe lo stesso caso, come se equals tornato true per un oggetto null.

1

Per quanto strano possa sembrare, ma non è sicuro. Prova aggiungere la Tok a TreeSet o TreeMap (come chiave) e otterrai una NullPointerException.Il problema è che l'implementazione di TreeSet è basata su TreeMap. Quando proverai ad aggiungere (null) la mappa sottostante proverà a mettere il tuo nullo che risulterà in NPE

+0

Chi dice che l'OP utilizza un 'TreeSet' o' TreeMap'? –

+0

@matt L'OP ha detto in uno dei suoi commenti che usa sort() che usa compateTo(). – AlexWien

+0

@AlexWien ... che in particolare implica che l'OP è _non_ utilizzando un 'TreeSet' o' TreeMap'. –

4

Non sono soddisfatto con altre risposte:
Non si dovrebbe verificare nulla in confronto a
Si richiede che lanci una NullPointerException, altrimenti si rovinerà su i tuoi alberi e hai difficoltà a capire perché la tua TreeMap non funziona.

Un metodo molto raccomandabile:

public int compareTo(Tok other) { 
    int thisRang = this.rang; 
    int otherRang = other.rang; 
    return (thisRang < otherRang ? -1 : (thisRang == otherRang ? 0 : 1)); 
    } 
    public int compareTo(Object other) { 
    return compareTo((Tok)other); 
    } 

seguito per renderlo perfetto classe Tok dovrebbe essere definitivo! (In caso contrario, si potrebbe avere problemi quando si sottoclasse da Tok. (Sun ha reso tale errore di classe Data)

final class Tok { 
    int rang; 
} 

Trattare con confrontare e uguale non è sempre facile, è possibile utilizzare al posto di alberi (TreeMap) una HashMap, allora non dovete implementare compareTo. si dovrebbe implementare hashCode, dove è sufficiente ritorni this.rang.

Infine la sua è altamente raccomandato, ma non obbligatorio per implementare equals()

public boolean equals(Object obj) { 
return obj instanceof Tok 
    && this.rang() == ((Tok) obj).rang; 
} 
+0

mettendo la matrice di oggetti Tok in Arrays.sort (tok []) produce comunque un'eccezione. –

+0

Sì, quale eccezione? Un Nullpointer? Questo è buono! Non dovresti avere un oggetto Null nel token []. Assicurati di non aggiungere oggetti nulli al tuo Tok [] – AlexWien

+0

Ma questo è un problema so che posso accorciare il mio Tok [] per non contenere alcun indice nullo tuttavia questo significa che ho bisogno di creare un altro array conteggio del numero di indici non nulli quindi copiare questa matrice in una matrice temporanea e ordinare quella.È solo per complesso voglio semplicemente mettere tutti gli elementi nulli al più grande e metterli alla fine. –

1

AUT Hor sta insistendo sul fatto che non vuole rimuovere i valori nulli dal suo token [].
Ecco un soultion che permette di ordinare con valori NULL, e non sta violando i contratti java

Per evitare che, si crea un compareTo all'interno di classe Tok che viola il contratto compareTo, si crea un NullSafeComparator esplicito:

/** 
* This comparator accepts null objects, 
* sorts ascending, null values are after non null values. 
*/ 
public static final class NullSafeComparator implements Comparator<Tok> { 
    public int compare(Tok o1, Tok o2) { 
     int r1 = Integer.MAX_VALUE; 
     int r2 = Integer.MAX_VALUE; 
     if (o1 != null) { 
      r1 = o1.rang; 
     } 
     if (o2 != null) { 
      r2 = o2.rang; 
     } 
     return (r1 < r2 ? -1 : (r1 == r2 ? 0 : 1)); 
    } 
} 

classe semplificato Tok (rimuovere la parola chiave static sua è usato per definire che tutto all'interno di un'unità classe di test):

public static class Tok { 
    int rang; 
    public Tok(int rang) { 
     this.rang = rang; 
    } 
    public String toString() { 
     return Integer.toString(rang); 
    } 
} 

Infine un ONU si prova a mostrare:

public void testSort() { 

    Tok[] toks = new Tok[5]; 
    toks[0] = new Tok(3); 
    toks[1] = new Tok(1); 
    toks[2] = null; 
    toks[3] = null; 
    toks[4] = new Tok(2); 



    Arrays.sort(toks, new NullSafeComparator()); 



    for (Tok tok: toks) { 
     System.out.println(tok); 
    } 
    assertEquals(1, toks[0]); 
    assertNull(toks[4]); 
} 

che darà il seguente risultato desiderato:

1 
2 
3 
null 
null 
+2

Chill out. 'Comparator's _are_ ha il permesso di essere nullo; [il JavaDoc lo dice esplicitamente] (http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html): _ "Diversamente da 'Comparable', un comparatore può facoltativamente consentire il confronto di null argomenti, pur mantenendo i requisiti per una relazione di equivalenza. "_ –

+0

Ok, rimuoverò lo sporco dal codice – AlexWien

0

Secondo il documentation:

Note that null is not an instance of any class, and e.compareTo(null) should 
throw a NullPointerException even though e.equals(null) returns false. 

Quindi, se si implementa un metodo null-sicuro, la sua il comportamento sarà inaspettato (alias incoerente con la documentazione e in modo compatibile con il resto dell'API).