Blog

#AstuceDeCode 19 : autoboxing et unboxing

Astuce de code
Share Button

L’autoboxing et l’unboxing sont des fonctionnalités très pratiques introduites avec Java 5 (bon d’accord j’ai un train de retard :-p ) cela dit elles ont des effets de bord non négligeables qu’il convient de ne pas ignorer, sous peine d’avoir des surprises.

 

 

 

Débloque les + belles offres tech en 10 mins

Autoboxing : attention aux performances

L’autoboxing se fait manière relativement transparente et présente dans les faits peu de risques à part au niveau des performances. En effet, si on écrit le code suivant :


Long l = 235L;

La JVM le compilera comme ceci :


Long l = Long.valueOf(235L);

En d’autres termes dans de nombreux cas un objet de type java.lang.Long sera instancié à chaque fois que vous écrirez la ligne ci-dessus. J’écris bien dans de nombreux cas car la méthode Long.valueOf contient un mécanisme de cache pour certaines valeurs.

Là où l’impact peut être réel est si on a deux objets qu’on va nommer MonObj1 et MonObj2. MonObj1 définit la méthode suivante :


public Long getMaVal1();

De son côté MonObj2 définit la méthode suivante :


public void setMaVal2(final Long val2);

Si maintenant on écrit le code suivant :


long val = monObj1.getMaVal1();
// Traitement qui ne modifie pas val ...
monObj2.setMaVal2(val);

En fait le code généré sera :


long val = monObj1.getMaVal1().longValue();
// Traitement qui ne modifie pas val ...
monObj2.setMaVal2(Long.valueOf(val));

En d’autres termes on est en train d’instancier de nouveaux objets de type java.lang.Long pour rien, ce qui à terme peut avoir un impact non négligeable sur le garbage collector si le traitement est souvent invoqué. Il serait par conséquent bien plus efficace d’écrire le code suivant :


Long val = monObj1.getMaVal1();
// Traitement qui ne modifie pas val ...
monObj2.setMaVal2(val);

De cette façon on n’a plus d’unboxing et d’autoboxing inutile.

L’unboxing : attention aux NullPointerException

L’unboxing est l’opération inverse de l’autoboxing. Supposons qu’on ait le code suivant :


Long val = Long.valueOf(25L);
// bla bla bla
long l = val;

Au niveau du code compilé par la JVM on aura le code suivant :


Long val = Long.valueOf(25L);
// bla bla bla
long l = val.value();

Maintenant, si vous me voyez venir, on aura un problème dans le cas suivant :


Long val = Long.valueOf(25L);
// bla bla bla qui passe à un moment val à null
long l = val;

Ce code va produire… une NullPointerException !!! En d’autre termes pour le rendre safe il faudrait écrire :


Long val = Long.valueOf(25L);
// bla bla bla qui passe à un moment val à null
long l = val == null ? 0L: val.longValue();

C’est un peu verbeux n’est-ce pas ? Bon comme je suis sympa j’ai écrit une classe GojulUnboxingUtils qui propose des méthodes qui vous permettent justement de simplifier la gestion de ce genre de cas. Mais quand même il ne faut pas le négliger en particulier quand vous faites de l’unboxing depuis un getter.

La comparaison de types boxés

L’autoboxing peut aussi avoir des effets assez « amusants », par exemple avec le programme ci-dessous :


Integer i1 = 52;
Integer i2 = 52;
Integer i3 = 736;
Integer i4 = 736;

System.out.println(i1 == i2);
System.out.println(i3 == i4);

Maintenant imaginez que vous lancez ce programme depuis une JVM Oracle ou OpenJDK. À votre avis quelle va être la sortie ? Si vous répondez :


true
true

… ou encore :


false
false

… vous avez tort ! La réalité est que ce programme affichera :


true
false

Cela vient du fait que la JVM implémente un mécanisme de cache pour certaines valeurs des types primitifs dans le cadre de l’autoboxing et de l’unboxing. Pas convaincu ? Regardez donc ici.

Il convient également de considérer que ce genre de programme peut avoir une sortie non déterministe suivant la JVM utilisée, en d’autres termes c’est assez funky.

Comparaison de types boxés et non boxés

Dans le cas où vous comparez un type boxé avec son équivalent non boxé, le type boxé sera tout d’abord unboxé puis la comparaison se fera. Autrement dit si vous tapez le code suivant :


int i1 = 255;
Integer i2 = Integer.valueOf(255);
System.out.println(i1 == i2);

En fait la JVM le compilera comme ceci :


int i1 = 255;
Integer i2 = Integer.valueOf(255);
System.out.println(i1 == i2.intValue());

Par conséquent attention aux risques de NullPointerException !

En bref

L’autoboxing et l’unboxing permettent certes de gagner du temps mais ont de nombreux effets de bord non négligeables. Par conséquent ils doivent être utilisés avec la plus grande prudence et pas dans des opérations de comparaison d’égalité ou de différence. À titre personnel je ne me sers de cette fonctionnalité que dans le code de mes tests unitaires pour gagner du temps, mais en dehors de ça j’évite. Les IDEs ont une fonctionnalité permettant d’activer des warnings en cas d’autoboxing et d’unboxing. N’hésitez pas à l’activer si besoin… ;-)

Débloque les + belles offres tech en 10 mins

Cet article vous a plu ? Vous aimerez sûrement aussi :

 

JulienJulien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.

Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.

Son Twitter - Son LinkedIn

Share Button

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>