UKOnline

Conversion de données

Java est très strict du point de vue des données et il est parfois intéressant de convertir une donnée d'un type primitif vers un autre type primitif. Il faut néanmoins faire attention lors de conversions car il y a risque de perdre de l'information. En effet, imaginons vouloir convertir un int qui vaut 1254 en un byte. Ça ne sera pas possible puisque qu'avec le type byte, on ne peut représenter que des valeurs comprises entre -127 et 128. Si on fait la conversion, on va donc perdre de l'information.

Type de conversion

Il y a deux types de conversion. Les conversions sans perte d'information et celles qui impliquent une perte d'information. Il n'y a pas de perte d'information lorsque la grandeur du nombre converti est conservée.

Conversion sans perte d'information

Il y a 19 conversions sans perte d'informations, reprises sur la figure 18. Ces conversions ne perdent jamais d'information du point de vue de la quantité, mais les conversions vers un type primitif décimal peuvent mener à une perte de précision. Par exemple, lorsqu'on convertit un int ou un long vers un double ou un float, les chiffres les moins significatifs peuvent être perdus, la grandeur du nombre restant la même.

De Vers
byte short, int, long, float, double
short int, long, float, double
char int, long, float, double
int long, float, double
long float, double
float double
Conversions sans perte d'information.

La figure 19 résume les conversions sans perte d'information. Pour qu'une conversion sans perte d'information soit possible entre deux types, il faut juste que vous puissiez les rejoindre en suivant les flèches. Les flèches pleines représentent les conversions sans perte de précision et les flèches pointillées celles où une perte de précision peut avoir lieu.

Résumé des conversions sans perte d'information
Résumé des conversions sans perte d'information.

Pour résumer, les conversions sans perte d'informations ne perdent pas d'information à propos de la grandeur du nombre. Les conversions d'un nombre entier vers un nombre entier ne perdent aucune information, ni précision; la conversion est parfaite. Enfin, les conversions de int vers float et de long vers float et double peuvent provoquer une perte de précision.

Si une perte de précision se produit, le programme continue à s'exécuter sans problème, aucune erreur d'exécution ne se produit; il faut donc être bien conscient que cela peut se produire.

On remarque également que l'on peut passer d'un char vers un int, long, float ou double. En effet, rappelez-vous, les caractères correspondent en fait à des nombres entiers positifs qui dépendent de l'encodage utilisé.

Mot réservé strictfp

Pour que la conversion du type float vers le type double se fasse sans perte de précision, il faut utiliser le mot réservé strictfp. L'exemple suivant montre une telle conversion sans perte de précision. Vous pouvez également y voir une conversion de int vers float avec perte de précision.

Conversion avec perte d'information

Il y a 22 conversions avec perte d'information. Il faut bien entendu essayer de les éviter puisqu'elles peuvent résulter en une perte d'information et de précision. Ces pertes ne se produisent pas dans tous les cas. Elles sont toutes reprises sur la figure 20. Ces pertes d'informations sont dues au fait qu'on passe d'un type de donnée qui utilise $n$ bits vers un autre qui en utilise $m$, avec $m$ étant strictement plus petit que $n$.

De Vers
short byte, char
char byte, short
int byte, short, char
long byte, short, char, int
float byte, short, char, int, long
double byte, short, char, int, long, float
Conversions avec perte d'information.

On remarque que la conversion du type short vers le type char provoque une perte d'information alors qu'ils utilisent tous les deux $16$ bits en mémoire. Cette perte n'est pas due à la place en mémoire, mais bien au caractère signé des short. En effet, les char s'étendent de 0 à 65535, tandis que les short s'étendent de -32768 à 32767 et la conversion d'un nombre négatif vers un caractère n'a pas de sens. Un raisonnement similaire explique que la conversion de char vers short peut également résulter en une perte d'information.

Conversion du type byte vers le type char

Il reste une conversion qu'on n'a pas encore traitée : il s'agit de la conversion du type byte vers le type char. On passe d'un type qui prend 8 bits en mémoire à un type qui en prend 16. On ne va pas entrer dans les détails de cette conversion, mais il faut savoir qu'elle est possible sans perte d'information.

Mais elle n'est pas pour autant classée parmi les conversions sans perte d'information car ce qui se passe en réalité, c'est que le byte est d'abord converti en un int sans perte d'information et que le int obtenu est ensuite converti en char avec perte d'information. Ce type de conversion est le seul qui est avec et sans perte d'information.

C'est un peu spécial, mais c'est ainsi, on n'y peut rien ! Il faut néanmoins en être conscient et bien se rappeler que si on part d'une donnée en byte, qu'on la convertit en char, puis qu'on reconvertit le résultat obtenu en byte, on retombe toujours sur la valeur initiale.

Type boolean

Enfin, vous aurez sans doute remarqué que le type boolean n'apparait dans aucun des tableaux. En effet, on ne peut convertir une donnée boolean vers aucun autre type primitif et cette interdiction est également valable dans l'autre sens.

Bien que dans d'autres langages de programmation, il y a une correspondance entre la valeur true et l'entier 1 et entre la valeur false et l'entier 0, ce n'est pas du tout le cas en Java.

Conversion implicite

Maintenant qu'on a vu les deux types de conversions qui existent, voyons quand celles-ci se produisent dans un programme Java. Il y a tout d'abord les conversions implicites qui se produisent automatiquement de manière implicite.

Conversion par affectation

Une conversion par affectation intervient lorsqu'une valeur d'un certain type est affectée à une variable d'un autre type. Lorsqu'on écrit x = y, la valeur de y va être convertie dans le type de la variable x. La conversion se fait donc à partir du type d'une expression vers le type d'une variable. Seules les conversions sans perte d'information sont possibles; les autres, ainsi que la conversion de byte vers char, vont produire une erreur lors de la compilation. Prenons par exemple les instructions suivantes :

L'exécution de ces dernières affiche 25.0 à l'écran. Pourquoi le .0 ? Car la valeur 25 de type int à été convertie en un float lors de l'affectation money = dollars. Cette conversion étant bien sans perte d'information, elle a pu se produire implicitement.

Maintenant, si on avait le contraire, c'est-à-dire une conversion par affectation du type float vers le type int. Dans ce cas, on aurait eu une erreur de compilation puisque cette conversion est avec perte d'information. Prenons le programme de la figure 21, une erreur se produit lors de la compilation. L'erreur est de type « possible loss of precision ». Le compilateur indique également le type de donnée présent et celui auquel il s'attend :

> javac ConversionError.java
ConversionError.java:7: possible loss of precision
found   : float
required: int
		dollars = money;           // Conversion de float vers int
		          ^
1 error
Le programme ConversionError.

Remarquez que si on avait utilisé le littéral 25.0 au lieu de 25.0F, on aurait eu une autre erreur de compilation. La différence entre les deux littéraux est que le premier est de type double, tandis que le second est de type float et, étant donné que la conversion de double vers float (le type de la variable money) est avec perte d'information, le compilateur aurait généré une erreur.

Promotion arithmétique

La promotion arithmétique intervient automatiquement lorsque certains opérateurs mathématiques doivent modifier leurs opérandes pour pouvoir exécuter l'opération. Par exemple, lorsque vous tentez de diviser un float par un int, la valeur du dénominateur sera promue en un type float avant la division, afin que le résultat de la division soit un float. Les opérandes d'un opérateur sont donc convertis vers le type requis par l'opérateur. Pour rappel, les opérateurs arithmétiques existent en quatre versions : double, float, long et int.

Soient les instructions suivantes :

L'exécution du programme affiche 5.0 à l'écran puisqu'un des deux opérandes de l'opérateur / est de type float, ce qui implique que le résultat sera également un float. La valeur de la variable denom est convertie en un float avant la division.

Quels sont les opérateurs qui vont convertir automatiquement leurs opérandes ? Il y a tout d'abord les opérateurs unaires -, +, -- et ++. Si l'opérande est de type byte, short ou char, il est automatiquement converti en un int. Dans tous les autres cas, il n'est pas converti et conserve son type.

Enfin, il reste les opérateurs +, -, *, /, %, <, <=, >, >=, == et !=. Pour ceux-ci, il faut regarder le type des deux opérandes, et appliquer les règles suivantes dans l'ordre :

  1. Si l'un des opérandes est de type double, l'autre est converti en double ;
  2. Si l'un des opérandes est de type float, l'autre est converti en float ;
  3. Si l'un des opérandes est de type long, l'autre est converti en long ;
  4. Dans tous les autres cas, les deux opérandes sont convertis en int.

Voyons maintenant un exemple dans lequel deux conversions implicites se produisent :

Dans cet exemple, on utilise l'opérateur + sur des opérandes de type float et int. L'opérande de droite est donc converti depuis le type int vers le type float. L'addition est ensuite effectuée en float et son résultat, qui est un float, est affecté à une variable de type double. Il y a donc deux conversion implicites lors de l'exécution de la troisième instruction : une promotion arithmétique suivie d'une conversion par affectation.

Conversion par le compilateur

Il reste un dernier type de conversion implicite, ce sont les conversions par le compilateur. Il s'agit de conversions avec perte d'information, mais qui sont autorisées et effectuées de manière implicite. Rappelez-vous que les conversions avec perte d'information ne résultent pas toujours en une perte de précision; et donc, si le compilateur est sûr qu'une telle perte n'aura pas lieu, il autorise la conversion à se faire.

Voyons un exemple où une telle conversion se produit :

La première instruction déclare une nouvelle constante B de type byte et l'initialise avec le littéral 50. Pour rappel, ce littéral est de type int et comme la conversion de int à byte est avec perte d'information, il ne s'agit pas d'une conversion par affectation.

Néanmoins, rappelez-vous également que les byte sont stockés sur 8 bits et sont donc compris entre -128 et 127. Le compilateur se rend compte que la valeur 50 est comprise entre les deux limites et que la conversion ne résultera donc pas en une perte de précision. Celle-ci est donc autorisée par le compilateur.

Ce genre de conversion ne se produit que pour des affectations de littéraux entiers de type int dans des variables de type byte ou short. Par exemple, les instructions suivantes ne compileront pas :

Le compilateur peut autoriser des conversions avec perte d'information lorsque l'expression affectée à la variable est une expression constante; c'est-à-dire soit un littéral, une constante ou une expression dont tous les opérandes sont des littéraux ou des constantes.

La troisième instruction de l'exemple est une expression constante. Les deux opérandes de l'expression B + S sont tous les deux des constantes. L'instruction short sum = B + S; compile donc sans erreur puisque le compilateur est capable de calculer que le résultat de la somme (25050) se trouve bien dans les limites du type short qui sont -32768 et 32767.

Si les deux variables B et S n'étaient pas des constantes, une erreur aurait été générée lors de la compilation puisque le compilateur n'est pas capable de calculer la valeur de l'expression B + S. Ainsi, le programme de la  22 ne compile pas et produit l'erreur suivante :

> javac ConversionConstant.java
ConversionConstant.java:8: possible loss of precision
found   : int
required: short
		short sum = B + S;
		              ^
1 error
Le programme ConversionConstant.

Cast ou conversion explicite

Les conversions qu'on vient de voir se font de manière implicite, automatiquement. Mais on ne peut faire que des conversions sans perte d'information. Il est néanmoins parfois utile de réaliser des conversions avec perte d'information ou de convertir un byte en un char. Pour cela, on doit faire un cast, aussi appelé conversion explicite.

On fait un cast grâce à l'opérateur de cast. Il s'agit d'un opérateur unaire, de priorité 3, associatif de droite à gauche. Grâce à cet opérateur, on va pouvoir convertir une donnée dans un type spécifié, que la conversion soit avec ou sans perte d'information.

L'opérateur est constitué de deux parenthèses entre lesquelles se trouve un type de donnée, précisément celui vers lequel la conversion doit avoir lieu. Ainsi, pour convertir une donnée en int, la forme prise par l'opérateur est (int).

L'exemple suivant montre plusieurs conversions utilisant l'opérateur de cast. Il y a une conversion de int vers float (sans perte d'information), de int vers byte (avec perte d'information) et enfin de byte vers char.

Voici ce qui est affiché à l'écran après exécution de ce programme :

292.0
36
$
36

On voit effectivement que la première conversion n'a pas fait perdre d'information et l'entier 292 est devenu le flottant 292.0. L'opérateur de cast n'est pas nécessaire puisque cette conversion aurait pu avoir lieu de manière implicite (conversion par affectation). On peut néanmoins l'utiliser pour mettre en évidence le changement de type, le rendre explicite.

La deuxième conversion provoque une perte d'information. En effet, comme vous pouvez l'observer sur le résultat produit, la valeur n'est plus la même puisqu'on passe de 292 à 36. Pour comprendre pourquoi on obtient la valeur 36, il faut comprendre comment les entiers sont représentés dans l'ordinateur; ce sujet est traité à la section suivante.

Enfin la troisième conversion va du type byte vers le type char. Aucune information n'est perdue, mais cette conversion ne peut se faire que de manière explicite. En effet, pour rappel, le byte est d'abord converti en un int qui est ensuite converti en char. Cette dernière conversion étant classée parmi les conversions avec perte d'information, il faut utiliser l'opérateur de cast.