1. Découverte de la classe Objects
Passons au sujet ! Si vous en avez assez de vérifier manuellement les valeurs sur null et d’écrire le calcul du hachage pour chaque champ, la classe utilitaire java.util.Objects vous sauvera la mise. Son rôle — rendre le travail avec les objets plus simple, plus concis et plus sûr.
C’est vraiment un « couteau suisse » : la classe sait comparer des objets pour l’égalité en toute sécurité (sans risque de NullPointerException), calculer commodément des codes de hachage, comparer via un comparateur et vérifier les arguments par rapport à null.
Objects.equals : comparaison sûre tenant compte de null
Si vous écrivez simplement a.equals(b), et que a vaut null, vous obtiendrez un NullPointerException. Les vérifications manuelles sont lourdes. Objects.equals(a, b) fait tout pour vous :
- Si les deux valent null — renvoie true.
- Si un seul vaut null — renvoie false.
- Si les deux ne sont pas null — appelle l’equals habituel.
import java.util.Objects;
String a = null;
String b = "Java";
System.out.println(Objects.equals(a, b)); // false
String c = null;
System.out.println(Objects.equals(a, c)); // true
String d = "Java";
String e = "Java";
System.out.println(Objects.equals(d, e)); // true
Pourquoi est-ce pratique ? Le code est plus court, plus clair et protégé des NPE accidentels.
2. Objects.hash et hashCode : calcul de hachage concis
En redéfinissant hashCode avec equals, il est facile de se tromper, surtout quand il y a beaucoup de champs. Le code manuel est souvent verbeux et fragile :
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
La méthode Objects.hash résout le problème — concis, sûr et gère null :
import java.util.Objects;
public class Person {
private String name;
private int age;
// ... constructeur, getters, etc.
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Point important : Objects.hash utilise des varargs et crée un tableau — dans de rares zones très sollicitées, un hashCode manuel peut être plus rapide. Pour la plupart des applications, la différence est négligeable.
3. Objects.compare : déléguer la comparaison à un comparateur
Parfois, il faut comparer deux objets à l’aide d’un Comparator préparé à l’avance. Au lieu d’appeler directement comparator.compare(a, b), vous pouvez utiliser :
int result = Objects.compare(a, b, comparator);
Cette méthode :
- Retourne 0 si les objets sont égaux.
- Considère null comme « inférieur » à tout objet non-null.
- Dans les autres cas, délègue la logique au comparateur passé.
import java.util.Comparator;
import java.util.Objects;
class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
Person a = new Person("Anna");
Person b = new Person("Boris");
Comparator<Person> byName = Comparator.comparing(Person::getName);
System.out.println(Objects.compare(a, b, byName)); // <0, car "Anna" < "Boris"
System.out.println(Objects.compare(a, null, byName)); // >0, car a != null
System.out.println(Objects.compare(null, b, byName)); // <0, car null < b
System.out.println(Objects.compare(null, null, byName)); // 0
}
}
4. Objects.requireNonNull : assurance contre les erreurs « invisibles »
Si une méthode doit n’accepter que des valeurs non-null, vérifiez-le immédiatement. Objects.requireNonNull lèvera un NullPointerException avec votre message :
public void setName(String name) {
this.name = Objects.requireNonNull(name, "Le nom ne peut pas être null");
}
5. Exemple : implémentation correcte de equals, hashCode et compareTo avec Objects
import java.util.Objects;
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = Objects.requireNonNull(name, "Le nom ne peut pas être null");
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) {
if (this == o) return true; // Comparaison par référence
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
// Comparaison sûre avec null
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); // Concis et sûr
}
@Override
public int compareTo(Person other) {
// D’abord comparer par le nom, puis par l’âge
int cmp = name.compareTo(other.name);
if (cmp != 0) return cmp;
return Integer.compare(age, other.age);
}
}
Vous pouvez maintenant stocker des objets dans un HashSet, les utiliser comme clés dans une HashMap, tester l’égalité et trier des listes (par exemple via Collections.sort).
6. Applications concrètes : réduction du code et diminution des erreurs
Exemple : liste d’utilisateurs dans une application
Grâce à une paire equals/hashCode correcte, la recherche dans les collections fonctionne de manière prévisible :
import java.util.ArrayList;
import java.util.List;
List<Person> users = new ArrayList<>();
users.add(new Person("Anna", 25));
users.add(new Person("Boris", 30));
Person search = new Person("Anna", 25);
System.out.println(users.contains(search)); // true
Exemple : travail avec des champs pouvant être null
Si une classe possède des champs qui peuvent être null (par exemple, le deuxième prénom), utilisez Objects.equals et Objects.hash :
import java.util.Objects;
public class User {
private String firstName;
private String middleName; // Peut être null
private String lastName;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(firstName, user.firstName)
&& Objects.equals(middleName, user.middleName)
&& Objects.equals(lastName, user.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, middleName, lastName);
}
}
7. Tableau : principales méthodes de la classe Objects
| Méthode | Finalité | Exemple d’utilisation |
|---|---|---|
|
Comparaison sûre de deux objets en tenant compte de null | |
|
Calcul concis du code de hachage sur plusieurs champs | |
|
Comparaison via un comparateur, sûre pour null | |
|
Vérification de null, lève NullPointerException | |
|
Test de null/non null (pratique avec le Stream API) | |
8. Erreurs types lors de l’utilisation des méthodes de la classe Objects
Erreur n° 1 : oubli d’utiliser Objects.equals pour les champs nullable. Si vous comparez les champs directement via equals, vous pouvez déclencher un NullPointerException. Utilisez Objects.equals(middleName, other.middleName).
Erreur n° 2 : tous les champs ne sont pas pris en compte dans hashCode. Les champs qui participent à equals doivent aussi participer à hashCode, sinon le comportement de HashSet/HashMap devient imprévisible.
Erreur n° 3 : hashCode manuel erroné. Respecter les coefficients et les vérifications sur null n’est pas trivial. Objects.hash le fait pour vous ; utilisez-le s’il n’y a pas d’exigences strictes de performance.
Erreur n° 4 : vous n’utilisez pas Objects.requireNonNull là où c’est le contrat de la classe. Si un champ ne doit pas accepter null, vérifiez-le dans le constructeur/le setter — l’erreur apparaîtra immédiatement, et non « dans les méandres » de la pile d’appels.
Erreur n° 5 : vous utilisez Objects.hash pour des tableaux. Pour les tableaux, utilisez Arrays.hashCode, et pour les tableaux imbriqués — Arrays.deepHashCode ; de même, pour comparer le contenu, utilisez Arrays.equals/Arrays.deepEquals.
GO TO FULL VERSION