« Programmation Java/Transtypage » : différence entre les versions

Contenu supprimé Contenu ajouté
mfff... le jour où un objet pourra changer de classe !...
Ligne 1 :
{{Programmation Java}}
 
Le '''transtypage''' (ou ''cast'') est la conversion d'unune objetexpression d'uneun classecertain type en une expression d'un autre classetype.
=== Transtypage implicite ===
 
On peut affecter à un champ ou une variable d'un type donné une expression de type moins élevé dans la hiérarchie des types. De même, une méthode ou un constructeur attendant un argument d'un type donné peut recevoir en argument effectif une expression de type moins élevé que celui indiqué dans sa déclaration. L'expression fournie sera dans ce cas automatiquement convertie en le type attendu, sans que l'utilisateur ait besoin d'expliciter cette conversion. Toute tentative de conversion implicite d'un type vers un type qui n'est pas plus haut dans la hiérarchie des types déclenchera une erreur au moins à l'exécution, ou dès la compilation si elle est détectable statiquement.
=== Transtypage explicite ===
==== Cas des types primitifs ====
La syntaxe du changement de type est la suivante :
Dans le cas des types primitifs, la hiérarchie est la suivante : byte est plus bas que short, short plus bas que int, char plus bas que int, int plus bas que long, long plus bas que float, float plus bas que double. Le type boolean est incomparable avec les autres types de base. On peut par exemple assigner à une variable de type float une expression de type int égale à 3 : l'expression 3 sera, avant affectation, convertie en float (3.0). Cette forme de conversion est réversible : on peut, après passage de int à float, reconvertir l'expression de type float résultante en int par une conversion explicite (voir ci-dessous) et retrouver la même valeur.
(''nouveau_type'')''terme''
Où <tt>''terme''</tt> est le terme de l'expression à convertir.
Si ce terme est une expression, il faut l'encadrer par des parenthèses.
 
Exemple :
<source lang="java">
int n;
long a=1000L;
float f;
int b=(int)(a*3L)+2; // multiplie '''a''' par 3,
n = 3;
// convertit en int, et ajoute 2
f = n; // 3 est converti en 3.0
</source>
Le type déterminé statiquement pour les arguments d'opérateurs arithmétiques est le premier type à partir de int dans lequel peuvent être convertis les types de tous les arguments. Les expressions constantes
Les types de base entiers, reéls et caractères peuvent être convertis dans un autre type de ce groupe (entiers, réels, caractères).
sont d'autre part typées statiquement en le premier type à partir de int permettant leur représentation.
Un booléen ne peut être converti.
<source lang="java">
short s;
s = 15; // <-- erreur générée, 15 est typé statiquement de type int
// s = (short) 15; fonctionne (conversion explicite)
s = s + s ; // <-- erreur générée, chaque sous-expression est convertie en int
// et l'expression s + s est typée de type int.
// s = (short) (s + s) fonctionne
int n;
 
n = s; // correct : conversion implicite
Un objet ne peut être converti en une autre classe, que si sa classe réelle (celle du constructeur utilisé) est compatible avec la nouvelle classe ou interface, c'est à dire :
n = n + 1L // <-- erreur générée, la sous-expression gauche est convertie en long
*La nouvelle classe est une classe parente (directe/indirecte) de celle de l'objet,
</source>
*Ou l'interface est implémentée par la classe de l'objet.
==== Cas des types références ====
 
La ''classe'' d'un objet ne peut évidemment être convertie : durant toute sa durée de vie, il s'agit toujours de la classe dans laquelle est défini le constructeur employé lors sa création. Le ''type d'une référence'' peut en revanche être converti selon les règles suivantes :
Exemple 1 :
* Si A est ancêtre de la classe B, alors toute expression de type "référence vers B" peut être implicitement convertie en le type "référence vers A".
* Si I est un interface implémentée par la classe B, toute expression de type "référence vers B" peut être implicitement convertie en le type "référence vers I".
* Si J est une interface étendant l'interface I, toute expression de type "référence vers J" peut être implicitement convertie en le type "référence vers I".
* Si une expression est de type "référence vers X", alors seules les méthodes visibles (par déclaration ou par héritage) dans X sont invocables sur cette expression.
Dans l'exemple ci-dessous, on fait pointer trois références a, i, j, b, c vers un même objet de classe C. Toutes les methodes vues dans C sont invocables sur c, mais seules les méthodes vues dans A (resp. I, J, B) sont invocables sur a (resp. i, j, b) :
<source lang="java">
class A { ... } // extends Object implicite
class B extends Ainterface I { ... }
interface J { ... }
class B extends A implements I { ... } // implémente I, descendante de A
class C extends B implements J { ... } // implémente I et J, descendante de A et B
...
A a; // de type "référence vers A"
...
I i; // de type "référence vers I"
J j; // de type "référence vers J"
B b; // de type "référence vers B"
C c; // de type "référence vers C"
c = new C(); // l'opérateur new renvoie une référence de type "référence vers C"
B objB = new B();
A objA = (A)objB; // la classe A est parente de la // vers l'objet créé, de classe BC
 
// la suite d'affectations suivante est valide
i = c; // C implémente I
j = c; // C implémente J
b = c; // B est ancêtre de C
i = b; // B implémente I
a = b; // A est ancêtre de B
a = c; // A est ancêtre de C
 
// chaque affectation ci-dessous déclenchera statiquement une erreur
j = b // <-- B n'implémente pas J
i = j // <-- J n'est pas une extension de I
b = a // <-- B n'est pas ancêtre de A
</source>
La même régle permet de convertir toute référence en une référence de type Object :
Exemple 2 :
<source lang="java">
class A extends ... implements ... { ... } // extends Object implicite
...
class B extends A { ... }
Object o = new A();
...
Object obj = new B();
// ^^ la classe réelle de obj est B,
// conversion en Object implicite
A objA = (A)obj; // la classe A est parente de la classe B
</source>
==== Cas des conversions vers String ====
Toute expression peut être convertie implicitement (ou explicitement) dans le type "référence vers String". Dans le cas ou cette expression n'est pas statiquement constante, il y a alors création dynamique d'un objet de classe String représentant la valeur de cette expression, et l'expression résultant devient une référence vers cet objet.
=== Transtypage explicite ===
Le type d'une expression peut également être explicitement converti avec la syntaxe suivante :
(''nouveau_type'')''expression''
Où <tt>''expression''</tt> est l'expression à convertir. S'il s'agit d'une expression composée, il faut l'encadrer par des parenthèses. La conversion explicite d'une expression doit être utilisée à chaque fois que l'on souhaite convertir une expression dans un type qui n'est pas plus haut dans la hiérarchie des types. Dans le cas des types numériques, cette conversion n'est sans pertes que si le type cible permet de représenter la même valeur. Dans le cas contraire, la valeur choisie dépend du type initial et du type cible. Dans le passage de float à int, la valeur choisie est par exemple la valeur entière de la valeur initiale :
<source lang="java">
int n;
int f;
 
n = 3;
=== Transtypage implicite ===
f = n; // f vaut 3.0
Pour les types de base, les conversions implicites sont les suivantes :
f = f + 1; // conversion de 1 en 1.0 et somme : f vaut 4.0
*Toute expression ou constante utilisant un type entier, excepté <tt>long</tt>, est implicitement convertit en <tt>int</tt>,<br/>Exemples :
n = (int) f; // n vaut 4
<source lang="java">
f = f + 1.5; // f passe à 5.5
short s;
sn = 15(int) f; // <--5.5 erreurest générée,arrondi 15en est5 de: typen intvaut 5.
s = (short)15;
s = s + s ; // <-- erreur générée, expression convertit en int
s = (short)(s + s);
</source>
Pour les types de références, la conversion est libre : une référence de type quelconque peut être explicitement convertie en toute référence dont le type permet de manipuler l'objet référencé, selon les règles ci-dessus. La non-validité de cette conversion ne sera en général pas détectable avant l'exécution :
*Si l'expression contient un terme de type <tt>long</tt>, alors le résultat sera de type <tt>long</tt>.<br/>Exemples :
<source lang="java">
interface I { ... }
long a = 1 + 2L;
longclass bA ={ 3... + a;}
class B extends A implements I { ... }
class C { ... }
...
Object o = new B(); // l'objet créé est de classe B
I i = (I) o; // valide : B implémente I
A a = (A) o; // valide : A est ancêtre de B
B b = (B) a; // valide
C c = (C) o; // invalide : C n'est pas ancêtre de B
</source>
Ces conversions "descendantes" sont bien sûr propices aux erreurs. L'opérateur instanceof permet de vérifier la validité d'une conversion avant de l'effectuer :
Pour les objets, la conversion en une classe parente peut être implicite.
Exemple :
<source lang="java">
if (r instanceof C) {
Object obj = new A();
c = (C) r
...
}
else {
// gestion de l'erreur
...
}
</source>
 
=== Autoboxing ===