Syndication : Atom 1.0  RSS 2.0
Blogs des développeurs   »   adiGuba:Blog

Article complet: java.util.Objects : Simple et efficace

05/02/2010

Permalink 10:45:39, Catégories: Java, 7 (Dolphin), Récapitulatif Java, 1723 mots   French (FR) , adiGuba

[Java] java.util.Objects : Simple et efficace


En jettant un oeil aux nouveautées du JDK7, j'étais tombé sur la classe java.util.Objects qui proposait quelques méthodes utilitaires concernant les opérations de base des objets.

On y retrouve surtout des méthodes permettant de gérer les valeurs null. A première vue il n'y a rien d'extraordinaire là dedans, et je dois dire que je ne m'y étais pas vraiment attardé...

Mais aujourd'hui je suis tombé sur un billet de Joe Darcy présentant ces méthodes un peu plus en détail.

Et mine de rien cela s'annonce bien plus pratique que ce que je ne l'avais soupçonné...

[Suite:]

Comme je le disais, on y retrouve surtout des méthodes permettant de gérer les valeurs nulles sans provoquer d'exception. Par exemple :

  • static boolean equals(Object a, Object b) pour comparer deux objets en gérant proprement les cas où les objets seraient null.
  • static boolean deepEquals(Object a, Object b) permet de faire la même chose, mais traitera les tableaux récursivement.
  • static <T> int compare(T a, T b, Comparator<? super T> c) suit la même logique, mais en utilisant une instance de Comparator.
  • static int hashCode(Object o) retournera simplement le hashCode() de l'objet, ou 0 s'il est null.
  • static String toString(Object o) pour appeler toString() sur un objet, en retournant "null" si l'objet est null.
  • static String toString(Object o, String nullDefault) pour appeler toString() sur un objet, en retournant nullDefault si l'objet est null

Donc comme je le disais, à première vue il n'y a rien d'extraordinaire.

Il s'agit pourtant de petit bout de code qu'on est amené à répéter assez souvent, et même si le code derrière tout cela n'est pas bien compliqué, les risques d'erreurs sont nombreux.

Mais surtout cela simplifiera grandement le code !

L'exemple de equals()

Reprennons par exemple ce bout de code de la FAQ Java : Pourquoi et comment redéfinir la méthode equals() ?

  @Override 
  public boolean equals(Object obj) { 
    // Vérification de l'égalité des références 
    if (obj == this) { 
      return true; 
    } 
 
    // Vérification du type du paramètre 
    if (obj instanceof TestClass) { 
      // Vérification des valeurs des attributs 
      TestClass other = (TestClass) obj; 
 
      // Pour les attributs de type primitif 
      // on compare directement les valeurs : 
      if (this.attribut1 != other.attribut1) { 
        return false; // les attributs sont différents 
      } 
 
      // Pour les attributs de type objets 
      // on compare dans un premier temps les références 
      if (this.attribut2 != other.attribut2) { 
        // Si les références ne sont pas identiques 
        // on doit en plus utiliser equals() 
        if (this.attribut2 == null 
            || !this.attribut2.equals(other.attribut2)) { 
          return false; // les attributs sont différents 
        } 
      } 
 
      // Si on arrive ici c'est que tous les attributs sont égaux : 
      return true; 
    } 
 
    return false; 
  } 
} 

Ce bout de code vérifie l'égalité des objets en se basant uniquement sur deux attributs.
Bien sûr on peut raccourcir le code, par exemple en utilisant l'opérateur ternaire ? : mais le résultat n'en est pas forcément plus lisible :

  @Override 
  public boolean equals(Object obj) { 
    // Vérification de l'égalité des références 
    if (obj == this) { 
      return true; 
    } 
 
    // Vérification du type du paramètre 
    if (obj instanceof TestClass) { 
      // Vérification des valeurs des attributs 
      TestClass other = (TestClass) obj; 
 
      return ( (this.attribut1 == other.attribut1) 
        && ( (this.attribut2 == other.attribut2) || (this.attribut2 != null && this.attribut2.equals(other.attribut2)) ) ); 
    } 
    return false; 
  } 

Et il ne s'agit que d'une comparaison de deux attributs...

Avec Java 7, la méthode Objects.equals() nous permettra de faire ceci, ce qui est bien plus clair et précis à mon avis :

  @Override 
  public boolean equals(Object obj) { 
    // Vérification de l'égalité des références 
    if (obj == this) { 
      return true; 
    } 
    
    // Vérification du type du paramètre 
    if (obj instanceof TestClass) { 
      // Vérification des valeurs des attributs 
      TestClass other = (TestClass) obj; 
      
      return Objects.equals(this.attribut1, other.attribut1) 
        && Objects.equals(this.attribut2, other.attribut2); 
    } 
    return false; 
  } 

En clair, toutes ces méthodes nous éviterons d'avoir à gérer des valeurs nulles et nous rendrons donc le code bien plus lisible et bien plus sûr...

Mais ce n'est pas tout, il reste encore deux petites méthodes tout aussi pratiques et insoupçonnables.

Générer un hashCode en tout simplicité

La première est Objects.hash(Object...). Elle prend en paramètre un nombre variable d'objet et génèrera une valeur de hashage de cet ensemble, qui pourra par exemple être utilisé pour implémenter la méthode hashCode().

Petit retour dans la FAQ : Pourquoi et comment redéfinir la méthode hashCode() ?

Actuellement donc pour générer un hashCode() correct par rapport à notre méthode equals() vue précédemment, il faudrait utiliser un code comme celui-ci :

  @Override 
  public int hashCode() { 
    // On choisit les deux nombres impairs 
    int result = 7; 
    final int multiplier = 17; 
 
    // Pour chaque attribut, on calcule le hashcode 
    // que l'on ajoute au résultat après l'avoir multiplié 
    // par le nombre "multiplieur" : 
    result = multiplier * result + attribut1; 
    result = multiplier * result 
        + (attribut2 == null ? 0 : attribut2.hashCode()); 
 
    // On retourne le résultat : 
    return result; 
  } 

On pourra bientôt se contenter d'utiliser Objects.hash() en lui spécifiant les attributs à prendre en compte :

  @Override 
  public int hashCode() { 
    return Objects.hash( this.attribut1, this.attribut2 ); 
  } 

Simple et efficace !

Ne pas accepter les valeurs nulles

Enfin, on disposera de deux méthodes nonNull() permettant de gérer les cas inverse, c'est à dire de provoquer une exception si son attribut est null.

En effet, la gestion des valeurs nulles est très importante mais souvent erroné. On retrouve bien souvent des codes qui n'acceptent pas de telles valeurs, mais qui ne gère pas le cas, par exemple :

class MyObject { 
  private final String id; 
  private final String name; 
  
  public MyObject(String id, String name) { 
    this.id = id; 
    this.name = name; 
  } 
} 

Si les attributs ne doivent pas être null, on doit s'assurer de cela au plus tôt afin de remonter le problème au plus tôt, ce qui permettra de le corriger plus rapidement. Concrètement il faut vérifier la nullité de ces valeurs :

class MyObject { 
  private final String id; 
  private final String name; 
  
  public MyObject(String id, String name) { 
    if (id==null) throw new NullPointerException("id"); 
    if (name==null) throw new NullPointerException("name"); 
    this.id = id; 
    this.name = name; 
  } 
} 

La classe Objects nous propose donc deux méthodes nonNull() nous permettant de gérer cela simplement :

  • static <T> T nonNull(T obj) provoquera un NullPointerException si obj est null.
  • static <T> T nonNull(T obj, String message) fera de même, mais en personnalisant le message de l'exception.

Les méthodes sont paramétrées afin de retourner l'objet passé en paramètre lorsqu'il n'est pas null, ce qui nous permet d'effectuer la vérification dès l'affectation :

class MyObject { 
  private final String id; 
  private final String name; 
  
  public MyObject(String id, String name) { 
    this.id = Objects.nonNull(id, "id"); 
    this.name = Objects.nonNull(name, "name"); 
  } 
} 

Simple et efficace...

Social Bookmarking:

                                     

Adresse de trackback pour cet article:

http://blog.developpez.com/htsrv/trackback.php?tb_id=8597

Commentaires, Trackbacks, Pingbacks:

Connectez-vous pour vous abonner à cet article:

Flux de commentaires pour cet article : Atom 1.0  RSS 2.0
Commentaire de: lunatix [Membre]
dans l'ensemble, ca ressemble fortement a ce que fait google avec guava
http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Objects.html


Permalien 05/02/2010 @ 11:44
Commentaire de: adiGuba [Membre] · http://adiguba.developpez.com
Oui et il y a la même chose dans les Jakarta Commons...

Le problème c'est qu'on ne peut pas forcément inclure une librairie juste pour cela, donc c'est pratique d'avoir cela en standard :D

a++
Permalien 05/02/2010 @ 11:51
Commentaire de: gifffftane [Membre]
Et concernant les tableaux, il y a déjà pas mal de méthodes similaires dans la classe Arrays, par ex. equals : http://java.sun.com/javase/6/docs/api/java/util/Arrays.html#equals(java.lang.Object[],%20java.lang.Object[]) et d'autres deepMachin.

Sur les objets simples, par contre, cette classe sera la bienvenue.

Permalien 05/02/2010 @ 14:00
Commentaire de: bouye [Membre]
Probablement un retour indirect des classes privées de JavaFX 1.x qui faisaient ce genre de choses. Bon, manque plus que l’opérateur Elvis dans Java8 et c'est bon...
Permalien 08/07/2011 @ 01:10

Vous devez être identifié pour poster un commentaire.

Liste des blogs

Rechercher

<  Novembre 2011  >
Lun Mar Mer Jeu Ven Sam Dim
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        

Syndiquez ce blog XML

Articles :

Commentaires :

 
 
 
 
Partenaires

Hébergement Web