28/05/2010

Point théorique Java : Conversions de primitives numériques


1) Règle générale

Les conversions élargissantes sont autorisées, qu'elles soient implicites ou explicites :
byte -> short -> int -> long -> float -> double
char -> int /

ex: int i = 4;
long l = i;
float f = (float)i;

Les conversions rétrécissantes explicites sont autorisées :
ex: int i = 4;
byte b = (byte)i;

contrex : char c = i; // illégal

2) Elargissant ne signifie pas qu'il n'y a pas de perte de précision
Il faut remarquer que si la conversion de toute valeur entière en valeur à virgule flottante est considérée comme une conversion élargissante, cela ne signifie pas pour autant qu'elle se fait sans perte de précision.
ex : System.out.println( ((int)(float)1234567890) - 1234567890); // ceci affiche 46

3) Cast de valeur flottante en valeur entière
Tout cast de double ou float en byte, short, ou char se fait en 2 étapes :
- cast de double ou float en int
- cast de int en byte short ou char

Ceci peut induire des effets de bord :
double D = 0x80000000p0;
long L = 0x80000000L;
System.out.println((short)D); // cette ligne affiche -1, car (int)D vaut 0x7FFFFFFF
System.out.println((short)L); // cette ligne affiche 0, car (int)L vaut -0x80000000

Rappel : Le cast d'un float ou un double supérieur à la plus grande valeur que peut contenir un int (0x7FFFFFFF) donne cette valeur.

4) Exceptions

Les conversion rétrécissante implicite sont autorisées quand les conditions suivantes sont remplies simultanément:
- il s'agit d'une assignation à une variable de type byte, short ou char ;
- les valeurs assignées sont des valeurs littérales ou finales ou des opérations de valeurs littérales ou finales;
- les valeurs assignées sont dans la fourchette d'existance de la variable à assigner (cela implique entre autre qu'elles sont entières et non à virgule)

Il s'agit d'un cas particulier fait pour faciliter le codage :
ex : byte b1 = 2; // ceci devrait être illégal car "2" est un entier, mais c'est légal
ex : short s = 2+2; // même chose
contrex : byte b2 = 128; // ceci est illégal car un byte ne peut contenir le nombre 128
contrex : char c = 5-6; // ceci est illégal car un byte ne peut contenir le nombre -1

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

Code :

  public class AClass {
public static void main(String[] args) {
byte b = 2;
char c = -1;
short s = -3;
int i = c;
double d = 2.0;
float f = 2.0;
}
}


Réponse :
Les lignes 4 et 8 provoquent des erreurs de compilation.

Explication :
- la ligne 3 devrait être illégale car l'expression "2" est un entier et ne devrait pas pouvoir être assigné à un byte sans cast explicite. Cependant nous sommes dans un des seuls cas particuliers où cette règle ne s'applique pas, à savoir la conversion rétrécissante implicite.

- la ligne 4 elle est illégale, car -1 est un entier qui ne peut pas rentrer dans un char (il est négatif et les char sont non-signés). Nous ne sommes donc pas dans le cas particulier de la ligne 3, et le cast implicite n'est pas autorisé.

- la ligne 5 est légale pour les mêmes raisons que la ligne 3.

- la ligne 6 est légale : il s'agit d'un cast élargissant implicite.

- la ligne 7 est légale : il s'agit de l'assignation d'un double à une variable de type double : il n'y a pas de cast

- la ligne 8 est illégale, car l'espression "2.0" est un double et qu'elle ne peut pas être assignée à une variable de type float sans cast explicite. L'exception des lignes 3 et 5 ne s'applique qu'aux valeurs entières.


Point théorique associé :
ici

Questions pièges Java n°1

Ce billet et l'un des premiers d'une série issue d'un test que j'avais réalisé en 2006 après avoir lu la spécification Java 1.5
Le test comprenait 125 "questions pièges" sur le langage Java.

Je pense que la plupart de ces questions sont toujours valables en 1.6 et peut-être en 1.7

Question 1 :
Dans le programme suivant, y a-t-il des lignes qui provoquent des erreurs de compilation ? Lesquelles :

  public class AClass {
public static void main(String[] args) {
byte b = 2;
char c = -1;
short s = -3;
int i = c;
double d = 2.0;
float f = 2.0;
}
}