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
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()<strong>.longValue()</strong>;
// Traitement qui ne modifie pas val ...
monObj2.setMaVal2(<strong>Long</strong><b>.valueOf(</b>val<b>)</b>);
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 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<strong>.value</strong><b>()</b>;
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 à <strong>null</strong>
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 à <strong>null</strong>
<strong>long l = val == null ? 0L: val.longValue();
</strong>
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.
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.
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<strong>.intValue())</strong>;
Par conséquent attention aux risques de NullPointerException !
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 :
Julien
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.
Les maladies inflammatoires chroniques de l’intestin ou "MICI" sont invisibles, mais leurs impacts sur la…
Depuis l'été, j'ai un Pixel qui intègre à la fois un TPU (Tensor Processing Unit)…
On se retrouve dans un nouvel article avec toutes les infos sur cette nouvelle saison…
Pourquoi l’inclusion numérique est essentielle : le point avec Mathieu Froidure. Dans un monde de…
Elles sont passées où les femmes dans la tech ? Entre le manque de représentation…