28/05/2010

Point théorique Java : Boxing Unboxing

1) Principe
A chaque type de primitive numérique ou booleéne est associée un Wrapper.
Les Wrappers sont : Boolean, Byte, Short, Character, Integer, Long, Float et Double.
Tous à l'exception de Boolean dérivent de Number.

Lors d'une assignation, d'un passage de paramètre à une fonction, d'une opération ou d'un cast explicite, on peut utiliser une valeur primitive à la place de son Wrapper et réciproquement.
La conversion qui intervient à ce moment là est appellée boxing (primitive -> wrapper) ou unboxing (wrapper->primitive)
Exemples :
Integer I1 = 4;
Vector MonVector = new Vector(); MonVector.add(2.0);
int i2 = I1 +7;
if (i2 > I1) ;
Byte b = (byte)i2;

2) Ce que le boxing/unboxing ne fait pas
Le Boxing / Unboxing peut convertir une primitive en son wrapper associé et réciproquement.
Le Boxing ne peut pas convertir une primitive en un wrapper associé à une autre primitive.
Long l = 2; // ceci n'est pas légal
Le Unboxing ne peut pas convertir un wrapper en une primitive associée à un autre wrapper.
byte b = (Integer)2; // ceci n'est pas légal
Le Boxing/Unboxing ne se fait que quand on passe explicitement un Wrapper (respectivement une primitive), là où une primitive (resp un Wrapper) est attendu.
Double f = 2.0, g= 2.0; // ici (f == g) renverait false car les objets sont comparés directement sans être unboxés
Float h = 2.0f; // ici (f == h) provoquerait une erreur de compilation

3) Mutualisation
Au lançement de la jvm, celle-ci crée un certain nombre de Wrappers par défaut.
Ces wrappers sont automatiquement utilisés lors du boxing des valeurs auxquelles ils correspondent.
Ainsi, lorsque l'on écrit :
Byte b1 = 1;
Byte b2 = 1;
On a b1 == b2. C'est à dire que les deux références b1 et b2 désignent le même objet.
Les Wrappers par défaut sont :
- Tous les Byte
- Les Character compris entre 0 et 127
- Les Short, Integer et Long compris entre -128 et 127

Questions pièges Java n°3 : réponse

Code :

class AClass {
private static void egale(Integer i1,Integer i2){
if (i1 == i2)
System.out.println(i1 + " == " + i2);
}
public static void main(String[] args) {
egale(4,4);
egale(200,200);
}
}


Réponse
Le programme affiche donc uniquement : "4 == 4"

Explication
Lorsque l'on passe une primitive (ici un int) à une fonction qui attend explicitement le Wrapper associé (ici un Integer), la jvm "boxe" la valeur.
Ce sont donc bien des Integer (et non des int) qui sont comparés à la ligne 3, java ne réalise pas de unboxing par défaut.

La comparaison de la ligne 3 devrait donc toujours renvoyer "faux".

Cependant, java mutualise les Integer dont les valeurs sont comprises entre -128 et 127. Les références i1 et i2 pointent donc :
- sur le même Wrapper lors du premier appel,
- mais sur 2 Wrappers différent lors du second appel.

Le programme affiche donc uniquement : "4 == 4"

Point théorique associé
ici

Questions pièges Java n°3

Question 3 :
Qu'affiche le programme suivant :

  class AClass {
private static void egale(Integer i1,Integer i2){
if (i1 == i2)
System.out.println(i1 + " == " + i2);
}
public static void main(String[] args) {
egale(4,4);
egale(200,200);
}
}