Inserito da Dustin Marx i
La maggior parte dei nuovi sviluppatori Java imparano presto che dovrebbero generalmente confrontare le stringhe Java utilizzandoString.Equals (Object) piuttosto che usare ==
. Questo è sottolineato e rinforzato per nuovi sviluppatori ripetutamente perché quasi sempre significa confrontare il contenuto della stringa (i caratteri effettivi che formano la stringa), piuttosto che l'identità della corda (il suo indirizzo in memoria). Io sostengo che si debba rafforzare l'idea che ==
può essere usato al posto di Enum.equals (Object) .Fornisco il mio ragionamento per questa affermazione nel resto di questo post.
Ci sono quattro ragioni per cui credo che l'utilizzo ==
per confrontare le enumerazioni Java è quasi sempre preferibile utilizzare il "uguale a" metodo:
1. La ==
sul enumerazioni fornisce lo stesso confronto atteso (contenuto) che è uguale a
2. La ==
su enumerazioni è senza dubbio più leggibile (meno verbose) che è uguale a
3. La ==
su enumerazioni è più sicuro di nulla, è uguale
4. La ==
su enum fornisce in fase di compilazione (statica) controllo piuttosto che di runtime verifica
La seconda ragione di cui sopra ("senza dubbio più leggibile") è ovviamente una questione di opinione, ma quella parte di "meno prolissa" possono essere concordati. La prima ragione generalmente preferisco ==
quando si confrontano le enumerazioni è una conseguenza di come il Java Language Specification descrive gli enum. Sezione 8.9 ("enumerazioni") afferma:
E 'un errore di compilazione per tentare di istanziare esplicitamente un tipo enum. Il metodo clone finale di Enum assicura che le costanti enum non può mai essere clonato, e il trattamento speciale da parte il meccanismo di serializzazione assicura che le istanze duplicate non sono mai creati come risultato di deserializzazione. Istanziazione riflettente di tipo enum è vietata. Insieme, queste quattro cose in modo che nessun caso di un tipo enum esistono oltre a quelli definiti dalle costanti enum.
Perché non c'è una sola istanza di ogni costante enum, è consentito di utilizzare l'operatore == al posto del metodo equals quando si confrontano due riferimenti a oggetti, se è noto che almeno uno di essi si riferisce ad una enum costante. (Il metodo equals in Enum è un metodo finale che invoca semplicemente super.equals sul suo argomento e restituisce il risultato, in modo da effettuare un confronto di identità.)
L'estratto dalle specifiche sopra indicato comporta e poi afferma esplicitamente che è sicuro di utilizzare la ==
all'operatore di confrontare due enumerazioni, perché non c'è modo che non ci può essere più di una istanza della stessa costante enum.
Il vantaggio quarto ==
oltre . equivale a
quando si confrontano gli enum ha a che fare con la sicurezza in fase di compilazione. L'uso di ==
forze rigorosi un assegno momento della compilazione di quello per . equivale
a causa Object.equalsQ (Object) devono, per contratto, prendere un arbitrario di oggetti
. Quando si utilizza un linguaggio a tipizzazione statica come Java, io credo a sfruttare i vantaggi di questa tipizzazione statica, per quanto possibile. Altrimenti, mi piacerebbe usare un linguaggio tipizzato in modo dinamico . Io credo che uno dei temi ricorrenti della Effective Java è proprio questo: preferiscono il controllo di tipo statico, quando possibile.
Per esempio, supponiamo che avevo un costume enum chiamato frutta
e ho cercato di confrontarla con la classe java.awt.Color . Utilizzando il ==
operatore mi permette di ottenere un errore di compilazione (inclusa preavviso nel mio preferito Java IDE) del problema. Ecco un elenco di codice che tenta di comparare un costume enum a una classe utilizzando il JDK ==
operatore:
/ **
* Indicare se Colore fornito è un cocomero.
*
* implementazione di questo metodo è commentato per evitare un errore di compilazione
* che legittimamente non permette == per confrontare due oggetti che non sono e
* non può essere la stessa cosa di sempre.
*
* @ . candidateColor param colore che non sarà mai un cocomero
* @ return non deve mai essere vero.
* /
public boolean isColorWatermelon (java.awt.Color candidateColor)
{
/ / Questo confronto di frutta di colore porterà ad errore di compilazione:
/ / errore: tipi di incomparabile: Frutta e colore
ritorno Fruit.WATERMELON == candidateColor;
} L'errore del compilatore è mostrato nello snapshot dello schermo che viene dopo.
Anche se io non sono un fan di errori, preferisco che siano presi staticamente al momento della compilazione, piuttosto che a seconda della copertura runtime. Se avessi usato il uguale
metodo di questo confronto, il codice avrebbe compilato bene, ma il metodo che restituisce sempre falso
falso perché non vi è alcun modo di una dustin.examples.Fruit
enum sarà pari ad un java.awt.Color
classe. Io non lo consiglio, ma qui è il metodo di confronto con eguali.
:
/ **
* Indica se Colore fornito è un lampone. Questa è una sciocchezza assoluta
* perché di un colore non può mai essere uguale ad un frutto, ma il compilatore lo permette
* controllo e solo una determinazione runtime può indicare che essi non sono
* uguali, anche se non potranno mai essere uguali. Ecco come NON fare le cose.
*
* @ param colore candidateColor che non sarà mai un lampone.
* {@ @ return false} codice. Sempre.
* /
public boolean isColorRaspberry (java.awt.Color candidateColor)
{
/ /
/ / Non fare questo: rifiuti di sforzo e di codice fuorviante !!!!!!!!
/ /
Fruit.RASPBERRY.equals ritorno (candidateColor );
} La cosa "bella" di cui sopra è la mancanza di errori di compilazione. Compila splendidamente. Purtroppo, questo si paga con un prezzo potenzialmente elevato.
Il vantaggio finale che ho elencato di usare ==
piuttosto che Enum.equals
quando si confrontano le enumerazioni è quello di evitare il temuto NullPointerException . Come ho detto nel efficace nel trattamento NullPointerException Java , io in genere come per evitare imprevisti NullPointerException
s. Vi è una serie limitata di situazioni in cui mi vogliono veramente l'esistenza di un nulla di essere trattato come un caso eccezionale, ma spesso preferisco un rendiconto più elegante di un problema. Un vantaggio di confronto con le enumerazioni ==
è che un nulla può essere paragonato ad un non nulla enum senza incontrare una NullPointerException
(NPE). Il risultato di questo confronto, ovviamente, è falso
.
Un modo per evitare la NPE durante l'utilizzo . equals (Object)
è quello di invocare il pari
contro un metodo enum costante o una nota non-null enum e quindi passare il enum potenziale di dubbia (forse nullo) come parametro per il pari
metodo .Questo è stato spesso fatto per anni in Java con le stringhe per evitare la NPE. Tuttavia, con l' ==
operatore, al fine di confronto non importa. Mi piace.
Ho fatto le mie argomentazioni e ora mi muovo su alcuni esempi di codice. L'elenco seguente è una realizzazione della frutta ipotetico enum accennato in precedenza.
Fruit.java
pacchetto dustin.examples;
public enum listato di codice seguente è una semplice classe Java che fornisce i metodi per rilevare se un particolare enum o un oggetto è un frutto certo. Avevo messo normalmente controlla come questi nella stessa enum, ma funzionano meglio in una classe separata qui per il mio scopo illustrativo e dimostrativo. Questa categoria comprende i due metodi illustrati in precedenza per il confronto di frutta
di colore
sia con ==
e uguale
. Naturalmente, il metodo che utilizza ==
per confrontare un enum ad una classe doveva avere quella parte commentata per compilare correttamente.
EnumComparisonMain.java
pacchetto dustin.examples;
public class EnumComparisonMain
{
/ **
* Indica se frutto fornito è un cocomero ({} @ codice vero o no
* ({@ code false}).
*
* @ param frutta candidateFruit che può o non può essere un cocomero, è nullo
* . perfettamente accettabile (! Bring It On)
* @ param {@ code true} se la frutta è fornito anguria; {@ code false} se
. frutta * fornito non è un cocomero
* /
public boolean isFruitWatermelon (candidateFruit frutta)
{
candidateFruit ritorno = = Fruit.WATERMELON;
}
/ **
* Indica se oggetto fornito è un Fruit.WATERMELON ({@ code true}) o
* non ({@ code false}).
*
* @ param oggetto candidateObject che può o non può essere un cocomero e può
nemmeno * essere una frutta!
* @ param {} @ codice vero se l'oggetto fornito è un Fruit.WATERMELON;
. * {@ code false} se l'oggetto fornito non è Fruit.WATERMELON
* /
public boolean isObjectWatermelon (Object candidateObject)
{
ritorno candidateObject == Fruit.WATERMELON;
}
/ **
* Indicare se Colore fornito è un cocomero.
*
* implementazione di questo metodo è commentato per evitare un errore di compilazione
* che legittimamente non permette == per confrontare due oggetti che non sono e
* non può essere la stessa cosa di sempre.
*
* @ . candidateColor param colore che non sarà mai un cocomero
* @ return non deve mai essere vero.
* /
public boolean isColorWatermelon (java.awt.Color candidateColor)
{
/ / Ho dovuto commentare confronto di frutta di colore per evitare errore di compilazione:
/ / errore: Tipi di incomparabile: Frutta e Colore
ritorno / * Fruit.WATERMELON == candidateColor * / false;
}
/ **
* Indica se frutto fornito è una fragola ({@ code true}) o meno
* ({@ falso codice}).
*
* @ param frutta candidateFruit che può o non può essere una fragola; null è
* perfettamente accettabile ( !. Bring It On)
* {@ @ return true} se il codice fornito è frutto di fragola; {@ code false} se
. frutta * fornito non è fragola
* /
public boolean isFruitStrawberry (candidateFruit frutta)
{
Fruit.STRAWBERRY ritorno == candidateFruit ;
}
/ **
* Indica se frutto fornito è un lampone ({@ code true}) o meno
* ({@ code false}).
*
* @ param frutta candidateFruit che può o non può essere un lampone; null è
* completamente e interamente inaccettabile, si prega di non passare il valore null, per favore,
* per favore, per favore.
* {@ @ return true} se il codice fornito è frutto lampone; {@ code false} se
. frutta * NON è fornita lampone
* /
public boolean isFruitRaspberry (frutta candidateFruit)
{
candidateFruit.equals ritorno (Fruit.RASPBERRY);
}
/ **
* Indica se oggetto fornito è un Fruit.RASPBERRY ({@ code true}) o
* non ({@ falso codice}).
*
* @ param oggetto candidateObject che può o non può essere un lampone e può
* o può ! anche non essere un frutto
* {@ @ return true} se il codice oggetto fornito è un Fruit.RASPBERRY; {@ code false}
. * se non è un frutto o un lampone
* /
public boolean isObjectRaspberry (Object candidateObject)
{
candidateObject.equals ritorno (Fruit.RASPBERRY);
}
/ **
* Indica se Colore fornito è un lampone. Questa è una sciocchezza assoluta
* perché di un colore non può mai essere uguale ad un frutto, ma il compilatore lo permette
* controllo e solo una determinazione runtime può indicare che essi non sono
* uguali, anche se non potranno mai essere uguali. Ecco come NON fare le cose.
*
* @ param colore candidateColor che non sarà mai un lampone.
* {@ @ return false} codice. Sempre.
* /
public boolean isColorRaspberry (java.awt.Color candidateColor)
{
/ /
/ / Non fare questo: rifiuti di sforzo e di codice fuorviante !!!!!!!!
/ /
Fruit.RASPBERRY.equals ritorno (candidateColor );
}
/ **
* Indica se frutto fornito è un vitigno ({@ code true}) o meno
* ({@ code false}).
*
* @ param frutta candidateFruit che può o non può essere un acino d'uva; null è
* perfettamente accettabile ( !. Bring It On)
* @ param {@ code true} se la frutta viene fornito un vitigno; {@ code false} se
frutta * fornito non è un vitigno.
* /
public boolean isFruitGrape (candidateFruit frutta)
{
Fruit.GRAPE ritorno. equals (candidateFruit);
}
} ho deciso di approccio dimostrazione delle idee catturati nei metodi di cui sopra tramite test di unità. In particolare, faccio uso di Groovy 's GroovyTestCase . Quella classe per l'utilizzo di Groovy-powered test delle unità è nel listato di codice successivo.
EnumComparisonTest.groovy
pacchetto dustin.examples
classe EnumComparisonTest estende GroovyTestCase
{
esempio EnumComparisonMain privato
void setup () {instance = new EnumComparisonMain ()}
/ **
* Dimostrare che, mentre nulla non è anguria, questo controllo non porta a
* NPE perché utilizza controllare anguria {@ code ==} invece di {@ code. equivale a}.
* /
testIsWatermelonIsNotNull void ()
{
assertEquals ("null non può essere anguria ", false, instance.isFruitWatermelon (null))
}
/ **
* Dimostrare che un passato in oggetto che è in realtà un travestito di frutta possono
* essere paragonato al enum ed è risultata essere pari.
* /
testIsWatermelonDisguisedAsObjectStillWatermelon void ()
{
assertEquals ("Oggetto avrebbe dovuto essere cocomero",
vero, esempio . isObjectWatermelon (Fruit.WATERMELON))
}
/ **
* Dimostrare che un passato in oggetto che è in realtà un travestito di frutta possono
* essere paragonato al enum ed è risultata essere non uguali.
* /
testIsOtherFruitDisguisedAsObjectNotWatermelon void ()
{
assertEquals ("Frutta travestiti da Object NON devono essere cocomero" ,
falso, instance.isObjectWatermelon (Fruit.ORANGE))
}
/ **
* Dimostrare che un passato in oggetto che è davvero un nulla può essere paragonato
* senza NPE al enum e che essi saranno considerati non uguali.
* /
testIsNullObjectNotEqualToWatermelonWithoutNPE void ()
{
assertEquals ("null, anche come oggetto, non è uguale a Cocomero ",
falso, instance.isObjectWatermelon (null))
}
/ ** Dimostrare che prova funziona quando frutta fornita è davvero anguria. * /
testIsWatermelonAWatermelon void ()
{
assertEquals ("Anguria previsto per frutta", vero, instance.isFruitWatermelon (Fruit.WATERMELON))
}
/ ** Dimostrare che frutta diversa anguria non è un cocomero. * /
testIsWatermelonBanana void ()
{
assertEquals ("Una banana non è un cocomero", false, instance.isFruitWatermelon (Fruit.BANANA))
}
/ **
* Dimostrare che, mentre nulla non è fragola, questo controllo non porta a
* NPE perché utilizza controllare fragola {@ code ==} invece di {@ code. equivale a}.
* /
testIsStrawberryIsNotNull void ()
{
assertEquals ("null non può essere fragola ", false, instance.isFruitStrawberry (null))
}
/ **
* Dimostrare che il caso lampone getta NPE a causa del tentativo di richiamare
* {@ code. equivale} metodo su null.
* /
void
/ **
* Dimostrare che il caso lampone getta NPE anche per oggetti a causa del
tentativo di richiamare * {@ code. equivale} metodo su null.
* /
void
/ **
* Dimostrare che {@ code. equivale} approccio funziona per confrontare le enumerazioni, anche se
il codice * {@. equivale} viene richiamato passata in oggetto.
* /
testIsRaspberryDisguisedAsObjectRaspberry void ()
{
assertEquals ("Si presume che sia oggetto di lamponi" ,
vero, instance.isObjectRaspberry (Fruit.RASPBERRY))
}
/ **
* Dimostrare che, mentre nulla non è uva, questo controllo non porta alla NPE
* perché controllare uva invoca {@ code. equivale} metodo sul enum costante
* a confronto piuttosto che sul codice fornito {} @ nullo.
* /
testIsGrapeIsNotNull vuoto ()
{
assertEquals ("null non può essere d'uva", false, instance.isFruitGrape (null))
}
} non mi descrivere quello che le varie prove dimostrano molto qui, perché i commenti sui metodi della classe testato e sul test metodi di coprire la maggior parte di questo. In breve, la varietà dei test dimostrano che ==
null-safe, e che è uguale
non è sicuro se non nullo richiamato su un costante e noto enum non nulli enum. Ci sono alcune prove altro per far sì che i confronti funzionano come ci si aspetterebbe loro di lavorare. L'output di eseguire il test di unità è indicato accanto.
Il GroovyTestCase
metodo shouldFail riferito che un NPE era effettivamente generata quando il codice è tentato di chiamareuguale
su un pubblicizzati enum che è stato davvero nullo. Perché ho usato la versione di overload di questo metodo che ha accettato l'eccezione eccezione come parametro, sono stato in grado di assicurare che si trattava di un NPE che è stata generata. L'istantanea schermata successiva mostra cosa avrei visto se avessi detto lo unit test che un IllegalArgumentException si aspettava piuttosto che una NullPointerException
.
Io non sono l'unico che pensa che ==
è preferibile alla pari
quando si confrontano gli enum. Dopo ho iniziato a scrivere questo (perché ho visto quello che ritengono essere un abuso di uguale
per confrontare due enumerazioni anche oggi per il tempo umpteeth), ho scoperto altri posti rendendo lo stesso argomento. Il post == uguale o con Java enum evidenzia la fase di compilazione vantaggio esplicitamente confronto di due tipi distinti con ==
: "Uso == il compilatore urla e ci dice che stiamo confrontando le mele con le arance". Ashutosh post 's Confrontando Java enumerazioni conclude che "utilizzando == finita. equals () è più vantaggioso in due modi" (evitando la NPE e ricevendo il statico in fase di compilazione controllo). Il post Quest'ultimo mi ha anche sottolineato l'eccellente punto di riferimento a confronto Java membri enum: == o equals ()? .
The View dissenzienti
Tutti e tre i riferimenti appena citati avuto pareri discordanti sia nel thread (l'ultimo riferimento) o come commenti al post sul blog (i primi due riferimenti). Gli argomenti principali di confronto con le enumerazioni pari
piuttosto che ==
è la coerenza con il confronto di altri tipi di riferimento (non primitive). C'è anche un argomento che è uguale
sia in un modo più leggibile, perché uno sviluppatore Java con esperienza di meno non credo che sia sbagliato e lui o lei potrebbe pensare ==
è una cattiva idea. Il mio argomento è che è bene conoscere la differenza e potrebbe fornire una preziosa esperienza di insegnamento. Un altro argomento contro ==
è che è uguale
viene compilato nel ==
forma comunque. Non vedo che così tanto come un argomento contro l'uso ==
. In effetti, è davvero un punto controverso nella mia mente se sono la stessa cosa in bytecode comunque. Inoltre, non toglie i vantaggi di ==
in termini di sicurezza nullo e controllo di tipo statico.
Non dimenticare! =
Tutte le argomentazioni sopra si applicano alla questione della ! =
contro ! Enum.equals (Object)
, naturalmente. I Tutorial Java sezione di ottenere e impostare i campi dei tipi di Enum dimostra confrontando gli stessi tipi enum e il loro esempio fa uso di ! =
.
Conclusione
C'è molto poco nello sviluppo di software che può essere affermato in modo inequivocabile e senza polemiche. L'utilizzo diuguale
rispetto a ==
per confrontare le enumerazioni non fa eccezione a questo. Tuttavia, non ho problemi a confronto primitive in Java (il tempo che mi assicurano che sono primitive vere e riferimenti non unboxing) con ==
e allo stesso modo non hanno alcun problema a confronto con le enumerazioni ==
neanche. La specifica incantesimi esplicitamente che ciò sia ammesso come fa il Tutorial Java (che usa ! =
piuttosto ! Enum.equals
). Io in realtà andare oltre e affermare che è preferibile. Se posso evitare NullPointerException
s ed ancora ottenere la risposta che voglio (due cose in confronto non sono uguali) e possono spostare il mio controllo degli errori fino al momento della compilazione tutti senza sforzo in più, mi prendo che si occupano quasi ogni volta.
Nessun commento:
Posta un commento
Nota. Solo i membri di questo blog possono postare un commento.