Come scrivere uguale() in Java
Entro la fine di questo articolo, tutte le informazioni richieste saranno trattate per poter scrivere il metodo best equals() nel linguaggio di programmazione Java. I dettagli dell'uguaglianza verranno trattati in termini di definizione matematica e alla maniera del linguaggio di programmazione Java al fine di coprire le informazioni fondamentali necessarie per utilizzare la migliore pratica del metodo equals( ) .
Definizione matematica di uguaglianza
Secondo Wikipedia , la definizione di uguaglianza matematica è
In matematica, l'uguaglianza è una relazione tra due quantità o, più in generale, due espressioni matematiche, affermando che le quantità hanno lo stesso valore, o che le espressioni rappresentano lo stesso oggetto matematico. L'uguaglianza tra A e B si scrive A = B e si pronuncia A uguale a B .
Definizione di uguaglianza in Java
Nel linguaggio di programmazione Java, uguaglianza significa semplicemente avere lo stesso valore. A volte significa letteralmente avere lo stesso valore e talvolta significa avere la stessa struttura in termini di proprietà dell'oggetto.
Quando è necessario verificare l'uguaglianza di due valori di tipo primitivo, viene utilizzato direttamente il segno di uguale come nell'esempio seguente;
int a = 5;
int b = 6;
int c = 5;
// Prints "false" since a and b do not have the same value.
System.out.println(a == b);
// Prints "true" since a and c have the same value.
System.out.println(a == c);
D'altra parte, a volte è necessario verificare l'uguaglianza di due oggetti. In termini di oggetti, ci sono due diversi approcci che vengono utilizzati in diverse fasi del processo di verifica dell'uguaglianza di due oggetti. Entriamo nei dettagli.
Uguaglianza referenziale
Nel linguaggio di programmazione Java, i valori effettivi degli oggetti sono archiviati da qualche parte nella memoria e sono puntati dai riferimenti agli oggetti che sono anch'essi archiviati da qualche parte nella memoria. Diamo un'occhiata al seguente frammento di codice;
// Create an object with "new" operator.
// Point to object with object reference "a".
Object a = new Object();
// Create another object reference.
// Point to the same object as the reference "a" is pointing.
Object b = a;
// Output is true.
System.out.println(a == b);
// Create another object with "new" operator.
// Point to object with object reference "b".
b = new Object();
// Output is false.
System.out.println(a == b);
In uno scenario del genere, "a" e "b" sono in realtà due riferimenti che puntano allo stesso oggetto, quindi si può dire direttamente che "a" è uguale a "b" poiché si ottiene l'uguaglianza referenziale.
Tuttavia, lo scenario opposto non è sempre valido. Due riferimenti diversi possono puntare a posizioni di memoria diverse, ma gli oggetti a cui puntano i riferimenti possono comunque essere uguali.
Nella vita normale, a volte le quantità vengono confrontate. Ad esempio, se tu hai due mele e il tuo amico ha due mele, allora il numero di mele che tu e il tuo amico avete è uguale.
D'altra parte, a volte le cose possono anche essere confrontate in termini di proprietà o struttura. Entriamo nei dettagli di questo scenario.
Uguaglianza strutturale
L'uguaglianza strutturale è il tipo di cui gli oggetti vengono confrontati in base agli stati delle loro proprietà. Per controllare questo tipo di uguaglianza è necessario uno strumento più sofisticato piuttosto che solo l'operatore “==”. È il metodo equals(). Viene dalla classe Object che si trova nel punto più alto della gerarchia di classi nel linguaggio di programmazione Java. Controlliamo l'imminente frammento di codice Java;
public class Car {
// Assume that we have corresponding constructor, accessors, mutators, etc.
private String brand;
private String model;
private Integer modelYear;
private Integer mileage;
}
Car car1 = new Car("Audi","R8", 2023);
Car car2 = new Car("Audi","R8", 2023);
Car car3 = new Car("Audi","R8", 2021);
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car3));
public boolean equals(Object obj) {
return (this == obj);
}
public class Car {
private String brand;
private String model;
private Integer modelYear;
private Integer mileage;
// Assume that we have corresponding constructor, accessors, mutators, etc.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear);
}
}
Sulla seconda riga iniziamo a controllare l'identità degli oggetti. Se il riferimento all'oggetto o è nullo o entrambi i riferimenti all'oggetto puntano agli oggetti che appartengono a classi diverse, allora si può dire direttamente che questi due oggetti non hanno la stessa identità e non sono uguali. In caso contrario, siamo finalmente in grado di verificare le effettive strutture degli oggetti.
Sulla terza riga del metodo, eseguiamo il cast del riferimento o al tipo di classe corrispondente poiché il metodo è sovrascritto dalla classe Object e ora sappiamo che appartiene alla classe Car.
Infine, sull'ultima riga del metodo, viene verificata la struttura di due oggetti e questa è la riga effettiva in cui viene definita l'identità. Semplicemente, se i modelli di marca e gli anni di modello sono gli stessi, allora questi due oggetti sono uguali. In altre parole, questa è l'uguaglianza strutturale.
Abbiamo imparato le proprietà dell'uguaglianza matematica alla scuola elementare. Alcune di queste proprietà devono essere valide anche per il metodo equals() . Significa che queste proprietà devono essere aggiunte agli ingredienti del metodo equals() . Vediamo queste proprietà in termini di uguaglianza matematica e in termini di uguaglianza in Java;
Proprietà riflessiva
Nella definizione matematica di proprietà riflessiva, per tutti i numeri reali x ;
x = x
Un numero è uguale a se stesso.
In termini di uguaglianza Java, un oggetto deve essere uguale a se stesso;
Car car1 = new Car("Audi","R8",2013);
System.out.println(car1.equals(car1));
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return mileage < car.mileage && brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear);
}
Proprietà simmetrica
In termini di definizione matematica della proprietà simmetrica, per tutti i numeri reali x e y ;
Se x = y , allora y = x
L'ordine di uguaglianza non ha importanza.
In termini di uguaglianza Java, se l'oggetto a è uguale all'oggetto b, allora l'oggetto b deve essere uguale all'oggetto a;
Car car1 = new Car("Audi","R8",2013);
Car car2 = new Car("Audi","R8",2013);
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car1));
public class Car {
private String brand;
private String model;
private Integer modelYear;
private Integer mileage;
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Car)) return false;
Car car = (Car) o;
return brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear);
}
}
public class SportCar extends Car {
private String engineType;
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof SportCar)) return false;
SportCar sportCar = (SportCar) o;
return engineType.equals(sportCar.engineType);
}
}
Car car1 = new Car("Audi", "R8", 2012, 1000);
SportCar car2 = new SportCar("Audi", "R8", 2012, 1000, "4.0");
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car1));
Proprietà transitiva
Nella definizione matematica di proprietà transitiva, per tutti i numeri reali x , y e z ;
se x = y e y = z , allora x = z
Due numeri uguali allo stesso numero sono uguali tra loro.
In termini di uguaglianza Java, se l'oggetto a è uguale all'oggetto b e l'oggetto b è uguale all'oggetto c, allora l'oggetto a deve essere uguale all'oggetto c;
SportCar car1 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("100RPM"));
Car car2 = new Car("Audi", "R8", 2012, 1000);
SportCar car3 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("101RPM"));
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car3));
System.out.println(car1.equals(car3));
public class SportCar extends Car {
private Turbo turbo;
@Override
public boolean equals(Object o) {
if (!(o instanceof Car)) return false;
if (!(o instanceof SportCar)) return o.equals(this);
return super.equals(o) && ((SportCar) o).turbo == turbo;
}
}
public class Turbo {
private String power;
public Turbo(String power) {
this.power = power;
}
}
SportCar car1 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("100RPM"));
Car car2 = new Car("Audi", "R8", 2012, 1000);
SportCar car3 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("101RPM"));
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car3));
System.out.println(car1.equals(car3));
Proprietà coerente
Questa è una proprietà diversa poiché non esiste matematicamente un equivalente esatto. Per questo abbiamo bisogno di ottenere lo stesso risultato per esecuzioni consecutive del metodo equals().
Diamo un'occhiata al seguente frammento di codice;
Car car1 = new Car("Audi", "R8", 2012, 1000);
Car car2 = new Car("Audi", "R8", 2012, 1000);
for (int i = 0; i < 100000; i++) {
System.out.println(car1.equals(car2));
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Car)) return false;
Car car = (Car) o;
Random random = new Random();
return brand.equals(car.brand) && model.equals(car.model) && (random.nextInt(10) + 2013) == car.modelYear;
}
Nota: questo esempio non mi soddisfa molto. Dopo un'ampia ricerca, lo sostituirò con uno migliore. È chiaramente affermato nella documentazione Java del metodo equals () che "più invocazioni di x.equals(y)ritorno coerente trueo ritorno coerente false". Ci deve essere un solido esempio di violazione per questa affermazione.
Proprietà del parametro nullo
Questo è il modo in cui scelgo di chiamare questa proprietà.
Si prega di guardare il seguente frammento di codice;
Car car1 = new Car("Audi", "R8", 2012, 1000);
Car car2 = null;
System.out.println(car1.equals(car2));
Ora è chiaro quali proprietà dovrebbe avere un'implementazione del metodo equals() per fornire un calcolo corretto dell'uguaglianza. Inoltre, abbiamo visto i possibili scenari di violazione, quindi ora possiamo evitarli per il bene della qualità del codice e della logica aziendale.
Oggi molti IDE forniscono meccanismi di generazione per generare le migliori implementazioni possibili del metodo equals() . È una buona idea seguirli per la maggior parte del tempo invece di implementare il metodo equals() . Vediamo un esempio da Intellij IDEA ;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear) && mileage.equals(car.mileage);
}
Nella prima riga viene verificata l'uguaglianza referenziale, poiché due riferimenti a oggetti possono puntare allo stesso oggetto.
Sulla seconda riga è assicurata la validazione della Null Parameter Property e anche la stessa condizione if funge da clausola guard if controllando il tipo di classe degli oggetti che vengono evidenziati dai riferimenti al fine di prevenire la violazione della Proprietà Transitiva e della Proprietà Simmetrica .
Sulla terza riga, il riferimento all'oggetto viene inserito nel tipo di classe corrispondente che è Car nel nostro esempio per raggiungere le proprietà dell'oggetto Car.
Infine, sulla quarta riga viene verificata l'uguaglianza in termini di uguaglianza strutturale.
Esistono altri modelli per la generazione del metodo equals() . Puoi anche andare a controllarli.
Se desideri scrivere l'implementazione da solo, tieni presente che l'implementazione deve garantire la validità per tutte le proprietà del metodo equals() .
Mentre arriviamo alla fine dell'articolo, c'è ancora una lacuna molto importante che deve essere colmata. Apri il tuo IDE e prova a generare il metodo equals () . Vedrai che non esiste alcuna opzione per generare solo il metodo equals() senza il metodo hashCode() . A causa di alcuni tipi di raccolta nel linguaggio di programmazione Java, è un'ottima pratica non separare questi due metodi l'uno dall'altro. Si dice anche nella documentazione ufficiale di Java che è generalmente necessario sovrascrivere il metodo hashCode() ogni volta che il metodo equals() viene sovrascritto. Vorrei fornire informazioni corrispondenti sull'hashing e sul metodo hashCode() in un altro mio articolo.
Inoltre, a volte potremmo non aver bisogno di attenerci alle proprietà del metodo equals() per alcuni casi eccezionali come le classi di entità di JPA. Merita anche un suo articolo.
Insomma
Tutti i punti importanti per implementare il metodo equals() corretto sono trattati in questo articolo. Ti consiglio anche di scrivere i test unitari corrispondenti per testare la convalida delle proprietà del metodo equals () . Non dimenticare di guardare le altre risorse che possono essere trovate sul web.
In primo luogo, sembra un argomento molto semplice e primitivo poiché proviene dai fondamenti del design orientato agli oggetti. Tuttavia, come si vede dai dettagli, è molto importante non saltare questi concetti di base e comprenderli appieno.

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































