|
Les instructions et les opérateurs |
Les blocs
if ... else, switch
while, do ... while,
for
Les expressions
Les opérateurs
Les conversions (casts)
Priorité des
opérateurs
Vous allez pouvoir parcourir très rapidement ce chapitre si vous connaissez déjà le C ou le C++ : C'est surtout à ce niveau que Java et le C se ressemblent le plus car leur jeu d'instructions et d'opérateurs sont très proches.
Un bloc se note entre { } et est un ensemble de déclarations de variables locales, d'instructions (if, switch, while, do, for, try, throw, synchronized ou return), d'affectations, d'appels de méthode ou de créations d'objets dans un ordre quelconque.
A partir de Java 1.1, un bloc peut aussi déclarer n'importe où des classes internes :{ TypeVariable variableLocale1; TypeVariable variableLocale2 = valeurOuExpression; TypeVariable variableLocale3 [ ]; TypeVariable variableLocale4, variableLocale5; // Instructions if, switch, do, while, for, try, throw, synchronized ou return // ou affectations, appels de méthode ou création d'objets suivis de points virgules ; // A partir de Java 1.1 : déclaration de classes internes }Une instruction peut être éventuellement vide (ne rien faire) : dans ce cas, elle se note ; . Ce type d'instruction est quelques fois utilisé comme corps des instructions de boucles.
TypeVariable est soit un type primitif, soit le nom d'une classe.
A partir de Java 1.1, une variable locale ou un paramètre peut être déclaré final, pour garantir que la valeur qu'ils contiennent ne sera pas modifiée.
Une variable locale ne peut être déclarée deux fois avec le même nom à l'intérieur d'un même bloc, ni porter le même nom qu'un des paramètres d'une méthode. De plus, dans le cas d'un bloc imbriqué dans un autre, une variable locale ne peut porter le même nom qu'une autre variable déclarée dans le bloc externe.
Une variable locale (ou un paramètre d'une méthode) variable1 déclarée dans un bloc d'une classe Classe1 peut porter le même nom qu'un champ de Classe1. Dans ce cas, le champ variable1 de Classe1 est caché par la variable locale variable1. Si vous voulez faire référence au champ de la classe dans le bloc, vous pouvez utiliser l'expression Classe1.variable1, si variable1 est un champ de classe (static), ou l'expression this.variable1 si variable1 est un champ d'instance. L'utilisation de this est développée plus loin dans ce chapitre.
Toute variable locale variable1 déclarée dans une classe Classe1 peut porter le même nom qu'une méthode de Classe1 ou le même nom qu'une classe.
Une fois que le contrôle quitte un bloc, toutes les variables locales qui y ont été déclarées n'existent plus. Par conséquent, dans un bloc, la portée d'une variable locale est limitée entre le point où elle est déclarée et la fin de ce bloc.
De façon similaire, les classes internes ont la même portée que des variables locales.
Les instructions if et switch ont la même syntaxe qu'en C/C++, c'est-à-dire :
if (expressionBooleenne) instructionOuBlocSiVrai if (expressionBooleenne) instructionOuBlocSiVrai else instructionOuBlocSiFaux switch (expressionEntiere) { case expressionConstante1 : instructionOuBlocCas1 break; // ou return; ou rien pour passer au case suivant case expressionConstante2 : instructionOuBlocCas2 break; // ou return; ou rien pour passer au case suivant // ... default : instructionOuBlocParDefaut break; // ou return; }Si les expressions expressionConstantei qui suivent chaque case ne sont pas constantes, vous devez utiliser l'instruction if.
instructionOuBloc... peut éventuellement contenir les instructions return; ou return valeur; pour sortir de la méthode courante (valeur doit être du type de retour de la méthode).
Les instructions de boucles ont la même syntaxe qu'en C/C++, c'est-à-dire :
while (expressionBooleenne) instructionOuBlocTant que l'expression expressionBooleenne est true, l'instruction ou le bloc d'instruction instructionOuBloc est exécuté.
do instructionOuBloc while (expressionBooleenne);Comme pour l'instruction while, mais l'instruction ou le bloc d'instruction instructionOuBloc est exécuté au moins une fois, puisque expressionBooleenne est vérifiée après la première exécution de instructionOuBloc.
for (expressionInit; expressionBooleenne; expressionIncrementation) instructionOuBloc for (TypeVariable variableLocale = valeurOuExpression; expressionBooleenne; expressionIncrementation) instructionOuBlocexpressionInit est exécuté puis tant que expressionBooleenne est true, l'instruction ou le bloc d'instruction instructionOuBloc puis expressionIncrementation sont exécutés.
instructionOuBloc peut éventuellement contenir les instructions suivantes :
- break; pour sortir de la boucle courante.
- continue; pour retourner au while ( ) ou au for ( ) courant sans terminer instructionOuBloc.
- return; ou return valeur; pour sortir de la méthode courante (valeur doit être du type de retour de la méthode).
L'équivalent de l'instruction for avec while est comme suit :
{ expressionInit; while (expressionBooleenne) { instructionOuBloc; expressionIncrementation; } }Cette équivalence est vraie sauf si instructionOuBloc contient l'instruction continue. En effet, après l'exécution d'un continue dans instructionOuBloc, l'instruction suivante à être exécutée est l'expression expressionIncrementation de l'instruction for, tandis que pour l'instruction while c'est l'expression expressionBooleenne.
La première instruction du for expressionInit, peut comporter la déclaration de plusieurs variables du même type, séparées par des virgules (,) ; la portée de ces variables est limitée aux expressions de l'instruction for et à instructionOuBloc. La troisième instruction expressionIncrementation peut elle aussi, être un ensemble d'instructions séparées par des virgules (,), comme dans l'exemple suivant manipulant deux indices dans un même for :
class Classe1 { int [ ] factorielles (int max) { // Création d'un tableau de max éléments int [ ] tableau = new int [max]; // Initialisation du tableau avec les max premières factorielles for (int i = 0, fact = 1; i < max; i++, fact = fact * i) tableau [i] = fact; return tableau; } }
Comme pour if, l'expression déterminant la poursuite d'une boucle while ou for doit être du type boolean.
for permet de déclarer une ou plusieurs variables locales, qui servent généralement d'incréments comme dans l'exemple : for (int i = 0; i < 5; i++) table [i] = 0;
Une expression peut prendre une des formes suivantes :
- Une valeur littérale d'un des types primitifs.
- Un accès à une variable locale au bloc ou à un paramètre d'une méthode, de la forme variable1.
- Un accès à un champ, de la forme champ1 ou expression.champ1.
expression est soit une classe ou une interface si champ1 est un champ de classe (static), soit une référence sur un objet de Classe1 si champ1 est un champ d'instance de la classe Classe1 ou d'une des super classes de Classe1.- Une expression pour accéder à un élément d'un tableau, de la forme tab [expression2] ou expression.tab [expression2], où expression2 doit être une expression convertible en int. tab peut être une variable locale, un champ de classe, ou un champ d'instance.
- this, super ou null.
- Une expression de création de tableau, utilisant l'opérateur new, de la forme new typeOuClasse [expression].
- Une conversion avec l'opérateur de cast, de la forme (typeOuClasse)expression.
- Une expression booléenne utilisant l'opérateur instanceof, de la forme objet instanceof Classe1.
- Une expression utilisant un opérateur arithmétique unaire (opunaire expression) ou binaire (expression1 opbinaire expression2).
Les expression suivantes peuvent être utilisées comme expression ou comme instruction, si elles sont suivies d'un point virgule (;) :
- Une expression utilisant un des opérateurs arithmétiques unaires ++ ou --, de la forme ++variable1, --variable1, variable1++ ou variable1--.
- Un appel à une méthode, de la forme methode (arg1, arg2, ...) ou expression.methode (arg1, arg2, ...).
expression est soit une classe ou une interface si methode () est une méthode de classe (static), soit une référence sur un objet de Classe1 si methode () est une méthode d'instance de Classe1 ou d'une des super classes de Classe1. Chacun des arguments peut être bien sûr une expression.
Si methode () est surchargée, la méthode appelée sera celle dont la déclaration correspond au type des arguments.
Si methode () est outrepassée, la méthode appelée par l'expression objet.methode () sera la méthode methode () de la classe d'appartenance de objet.- Une expression de création d'objet, utilisant l'opérateur new, de la forme new Classe (argConstructeur).
Si Classe a plusieurs constructeurs qui sont surchargées, le constructeur appelé sera celui dont la déclaration correspond au type des arguments.- Une affectation avec l'opérateur = ou les opérateurs d'affectation composés op= (ou op peut être un des opérateurs suivants : *, /, %, +, -, <<, >>, >>>, &, ^ ou |). Dans ce cas, l'expression x op= y est équivalente à x = x op y.
Contrairement au C/C++, lors de l'appel d'une méthode en Java, les arguments sont obligatoirement évalués l'un après l'autre de gauche à droite avant d'être passés à la méthode. Ceci permet d'éviter des effets de bords imprévisibles. Par exemple, l'expression methode (i = 1, i++, ++i) sera évaluée comme methode (1, 1, 3).
A part les appels aux méthodes, les créations d'objet, les affectations, l'utilisation des opérateurs ++ et --, toutes les expressions Java doivent être utilisées (comme argument ou comme valeur à affecter, par exemple). Vous ne pouvez écrire val1 * val2; tout seul. Ceci permet de repérer des erreurs d'écritures du type i == 0; qui ne font rien (suite à une opération Copier/Coller trop rapide).
L'opérateur virgule (,) de séparation d'instructions du C, n'existe pas en Java, excepté pour éventuellement la première et la troisième instruction d'un for, et pour séparer la déclaration de plusieurs variables de même type (comme par exemple int x, y;). Vous pouvez très bien l'éviter en rassemblant les instructions dans un bloc.
Les expressions constantes utilisées après un case ou pour initialiser les constantes d'une interface, sont des expressions ne pouvant utiliser que des opérateurs arithmétiques avec des valeurs littérales ou des champs ou variables locales final initialisées avec des expressions constantes.Utilisation de this et de super
Si dans une méthode non static methode1 () d'une classe Classe1, vous avez besoin d'une référence de l'objet sur lequel methode1 () a été invoqué, vous pouvez utiliser le mot clé this. Ceci est illustré dans l'exemple suivant :
class Classe1 { boolean methode1 () { return equals (this); } } class Classe2 { void methode () { Classe1 objet1 = new Classe1 (); objet1.methode1 (); } }La méthode equals () de la classe Object requiert comme paramètre une référence sur un objet. Ici, l'argument this lui est passé, qui en fait désigne objet1 (au passage le résultat est forcément true). this ne peut être utilisé que dans des méthodes non static (une méthode static n'est pas invoquée sur un objet).
Pour bien comprendre ce que représente this et l'appel d'une méthode sur un objet, voici un exemple de programme C (et non C++), avec une traduction possible en Java :
/* Définition d'un type de structure */ * utilisant un int */ typedef struct { int unEntier; } Type1; void modifier (Type1 *objet1, int var) { objet1->unEntier = var; } /* Fonction créant un objet de type */ * Type1 et appelant modifier () */ void uneMethode () { Type1 *objet1 = malloc (sizeof (Type1)); modifier (objet1, 10); } // Déclaration d'une classe // utilisant un int class Type1 { int unEntier; void modifier (int var) { // this est facultatif this.unEntier = var; } } class Classe1 { // Méthode créant un objet de classe // Type1 et appelant modifier () void uneMethode () { Type1 objet1 = new Type1 (); objet1.modifier (10); } }
A l'appel objet1.modifier (10); plusieurs opérations sont effectuées implicitement :
- La classe Type1 de objet1 est recherchée.
- La méthode modifier () de Type1 est appelée avec son argument 10.
- La "variable" this est initialisée avec objet1 dans la méthode modifier ().
this peut être utilisé seul pour désigner une référence d'un objet, instance de la classe Classe1 ou suivi de l'opérateur point (.) pour accéder aux champs et méthodes d'instance d'un objet. Ceci peut être utile pour distinguer un champ et un paramètre (ou une variable locale) qui ont le même nom, comme dans l'exemple suivant :class Classe1 { int var; int var2; Classe1 (int var) // le paramètre var cache le champ var { this.var = var; var2 = var + 1; // équivalent à this.var2 = var + 1; } }En fait, à chaque fois que vous utilisez une méthode ou un champ non static de la classe Classe1, à l'intérieur d'une des méthodes de Classe1, vous utilisez implicitement this, mais pour des commodités d'écriture, il ne vous est requis de l'écrire que pour éviter toute confusion.
A partir de Java 1.1, une nouvelle utilisation de this a été introduite pour distinguer si besoin est, l'instance d'une classe interne ClasseI de l'instance de la classe Classe0 dans laquelle ClasseI est déclarée : ClasseI.this désigne l'instance de la classe interne ClasseI et Classe0.this désigne l'instance de la classe Classe0.
Pour référencer l'objet de la super classe de this, on utilise super. Dans la pratique, super est surtout utilisé pour invoquer explicitement la méthode outrepassée d'une super classe.
Un exemple d'utilisation de super est donné au chapitre sur la création des classes.
L'opérateur -> n'existe pas. Donc, comme pour toute référence en Java, this est suivi de l'opérateur point (.) pour accéder aux champs et méthodes d'instance d'un objet.
Contrairement au C++, this ne peut être la variable de destination d'une expression d'affectation, c'est-à-dire que par exemple, Java n'accepte pas l'expression this = unObjet.
Opérateurs arithmétiques
++, --
Opérateurs de post incrémentation et post décrémentation : variable++ ajoute 1 à variable, et renvoie la valeur de variable avant que celle-ci soit incrémentée.
variable-- retire 1 à variable, et renvoie la valeur de variable avant que celle-ci soit décrémentée.++, --
Opérateurs de pré incrémentation et pré décrémentation : ++variable ajoute 1 à variable, et renvoie la nouvelle valeur de variable.
--variable retire 1 à variable , et renvoie la nouvelle valeur de variable.+, -
+expression n'a aucun effet sur expression, -expression renvoie la valeur opposée de expression.
~
~expressionEntiere renvoie le complément de expressionEntiere, bit à bit. Par exemple, ~0xA7 (10100111 en binaire) est égale à 0x58 (01011000 en binaire).
!
!expressionBooleenne renvoie le complément logique de expressionBooleenne, c'est-à-dire false si expressionBooleenne est égale à true, et inversement.
*, /
expression1 * expression2 renvoie la multiplication de expression1 par expression2.
expression1 / expression2 renvoie la division de expression1 par expression2, ou déclenche une exception ArithmeticException, si expression2 est nulle et la division s'opère sur des expressions entières.%
expression1 % expression2 renvoie le reste de la division de expression1 par expression2, ou déclenche une exception ArithmeticException, si expression2 est nulle et les expressions sont des entiers.
+, -
expression1 + expression2 renvoie l'addition de expression1 et de expression2.
Si l'un des opérandes expression1 ou expression2 est de la classe String, l'autre opérande est converti en String et le résultat est la concaténation des deux chaînes.
expression1 - expression2 renvoie la valeur de expression1 + (-expression2).<<, >>, >>>
expressionEntiere << nbreDecalage renvoie la valeur de expressionEntiere dont les bits sont décalés à gauche, nbreDecalage fois.
expressionEntiere >> nbreDecalage renvoie la valeur de expressionEntiere dont les bits sont décalés à droite, nbreDecalage fois, avec extension du bit de signe.
expressionEntiere >>> nbreDecalage renvoie la valeur de expressionEntiere dont les bits sont décalés à droite, nbreDecalage fois, avec introduction de zéro à gauche.<, >, <=, >=
expression1 < expression2 renvoie true si expression1 est strictement inférieure à expression2.
expression1 > expression2 renvoie true si expression1 est strictement supérieure à expression2.
expression1 <= expression2 renvoie true si expression1 est inférieure ou égale à expression2.
expression1 >= expression2 renvoie true si expression1 est supérieure ou égale à expression2.==, !=
expression1 == expression2 renvoie true si expression1 est égale à expression2. Si expression1 et expression2 sont des références sur des objets, le résultat est true si les 2 références désignent le même objet, ou si elles sont égales à null.
expression1 != expression2 renvoie true si expression1 est différente de expression2. expression1 != expression2 est équivalent à !(expression1 == expression2).&, ^, |
expressionEntiere1 & expressionEntiere2 renvoie le résultat d'un ET bit à bit entre les opérandes.
expressionEntiere1 ^ expressionEntiere2 renvoie le résultat d'un OU exclusif bit à bit entre les opérandes.
expressionEntiere1 | expressionEntiere2 renvoie le résultat d'un OU bit à bit entre les opérandes.
Les opérandes peuvent être éventuellement de type boolean.&&, ||
expressionBooleenne1 && expressionBooleenne2 renvoie le résultat d'un ET logique entre les opérandes. Si expressionBooleenne1 est false alors expressionBooleenne2 n'est pas évaluée, et false est tout de suite renvoyé.
expressionBooleenne1 || expressionBooleenne2 renvoie le résultat d'un OU logique entre les opérandes. Si expressionBooleenne1 est true alors expressionBooleenne2 n'est pas évaluée, et true est tout de suite renvoyé.? :
expressionBooleenne1 ? expression1 : expression2 renvoie la valeur de expression1 si expressionBooleenne1 est true, sinon la valeur de expression2.
x = expressionBooleenne1 ? expression1 : expression2; est équivalent à :if (expressionBooleenne1) x = expression1; else x = expression2;
En Java, les opérandes de l'opérateur % peuvent être des entiers ou des nombres flottants.
Java a deux opérateurs de décalages à droite >> et >>>. >> recopie à gauche le bit de signe tandis que >>> introduit des zéros à gauche.
L'opérateur instanceof
L'opérateur instanceof permet de vérifier si une référence désigne un objet d'une certaine classe ou d'une certaine interface. Il s'utilise de la manière suivante : objet instanceof Classe1 et le résultat est true si objet désigne une instance de Classe1 ou des classes dérivées de Classe1 , et false sinon. Si objet est égal à null, le résultat est toujours false.
Cet opérateur est surtout utile pour évaluer la classe Classe1 d'un objet dont la référence est d'une super classe de Classe1, comme dans l'exemple suivant :class Classe0 { } class Classe1 extends Classe0 { } class UneClasse { void methode () { Classe0 objet1 = new Classe1 (); Classe0 objet2 = new Classe0 (); Object objet3 = new Classe0 (); Object objet4 = null; boolean resultat; resultat = objet1 instanceof Classe1; // true resultat = objet1 instanceof Classe0; // true resultat = objet2 instanceof Classe1; // false resultat = objet3 instanceof Classe0; // true resultat = objet4 instanceof Object; // false } }Par extension, objet instanceof Object est toujours égal à true si objet est différent de null (toute classe hérite de Object et donc tout objet est une instance d'une classe dérivée de Object).
Java introduit l'opérateur instanceof qui permet de vérifier si une référence désigne un objet qui est une instance d'une classe ou d'une interface donnée.
Opérateurs du C/C++ absent en Java
OPERATEUR
DESCRIPTION Opérateur primaire d'accès aux pointeurs
Opérateur primaire d'accès aux classes
Opérateurs unaires pour les pointeurs
Opérateur de destruction d'objet
Les liens définis dans le tableau désignent les endroits où il est traité de l'absence de ces opérateurs.
Il existe 5 contextes dans lesquels une conversion peut avoir lieu :
- Les conversions intervenant dans une affectation d'une expression, variable = expression, où le type de expression doit être converti dans le type de variable.
- Les conversions appliquées sur chacun des arguments d'une méthode pour que leur type correspondent à celui déclaré par chacun des paramètres de la méthode. Ces conversions se font de la même manière que pour les affectations.
- Les conversions explicites du programmeur par un opérateur de cast. Celles-ci permettent d'effectuer n'importe quelle conversion d'un type dans un autre, si ce sont des types primitifs non booléen, et d'une classe dans une autre classe, si ces deux classes ont un lien d'héritage entre elles.
- Les conversions en chaîne de caractères permettent de convertir n'importe quel type en un objet de classe String.
- Les conversions nécessaires pour que dans une opération numérique comme oper1 op oper2, les opérandes oper1 et oper2 soient convertis dans un même type pour que l'opérateur op puisse être appelé :
- Si l'un des deux opérandes est du type double, l'autre est converti en double.
- Si l'un des deux opérandes est du type float, l'autre est converti en float.
- Si l'un des deux opérandes est du type long, l'autre est converti en long.
- Sinon, les deux opérandes sont convertis en int.
Conversions entre types primitifs avec gain de précision
- Conversions de byte en short, int, long, float ou double.
- Conversions de short en int, long, float ou double.
- Conversions de char en int, long, float ou double.
- Conversions de int en long, float ou double.
- Conversions de long en float ou double.
- Conversions de float en double.
Toutes ces conversions ne font perdre aucune précision au nombre converti. Elles ne requièrent aucun opérateur de cast, le compilateur effectue automatiquement ces conversions, mais pour clarifier votre code, vous pouvez utiliser un cast.
Conversions entre types primitifs avec perte de précision
- Conversions de byte en char.
- Conversions de short en byte ou char.
- Conversions de char en byte ou short.
- Conversions de int en byte, short ou char.
- Conversions de long en byte, short, char ou int.
- Conversions de float en byte, short, char, int ou long.
- Conversions de double en byte, short, char, int, long ou float.
Ces conversions ne sont autorisées par le compilateur que si le nombre à convertir est converti explicitement, grâce à l'opérateur de cast qui s'utilise ainsi : nombreType1 = (type1)nombreType2.
Conversions de références d'une classe dans une autre
Il faut bien percevoir l'intérêt de ces
conversions car c'est grâce à l'héritage,
à la possibilité
d'outrepasser des
méthodes et grâce à ces conversions que Java
(comme tout langage orienté objet) permet de mettre en
pratique les concepts de la programmation orientée objet.
Comme ce sont des casts implicites, on ne s'aperçoit pas
forcément qu'on les utilise...
Voici des exemples de cast implicite entre
références :
interface InterfaceX { } interface InterfaceZ extends InterfaceX { } class ClasseX implements InterfaceX { } class ClasseY extends ClasseX { } class ClasseZ implements InterfaceZ { } class UneClasse { void uneMethode () { // Création d'un objet de chaque classe ClasseX objetClasseX = new ClasseX (); ClasseY objetClasseY = new ClasseY (); ClasseZ objetClasseZ = new ClasseZ (); // Création de tableaux d'un élément ClasseX [] tabClasseX = new ClasseX [1]; ClasseY [] tabClasseY = new ClasseY [1]; ClasseZ [] tabClasseZ = new ClasseZ [1]; InterfaceZ [] tabInterfaceZ = new InterfaceZ [1]; // Cast implicite sur les objets ClasseX objetX = objetClasseY; // 1. cast de ClasseY vers ClasseX Object objet = objetClasseY; // cast de ClasseY vers Object InterfaceX objetInterfaceX = objetClasseX; // 2. cast de ClasseX vers InterfaceX objetInterfaceX = objetClasseY; // cast de ClasseY vers InterfaceX // classeY dérive de classeX // donc classe Y implémente aussi InterfaceX InterfaceZ objetInterfaceZ = objetClasseZ; // 3. cast de ClasseZ vers InterfaceZ objetInterfaceX = objetInterfaceZ; // cast de InterfaceZ vers InterfaceX objet = objetInterfaceX; // 4. cast de InterfaceZ vers Object objet = tabClasseY; // cast d'un tableau vers Object objet = tabInterfaceZ; // cast d'un tableau vers Object // 5. Les mêmes cas de cast mais sur les tableaux ClasseX [] tabObjetsX = tabClasseY; Object [] tabObjets = tabClasseY; InterfaceX [] tab2InterfaceX = tabClasseX; tabInterfaceX = tabInterfaceZ; InterfaceZ [] tabInterfaceZ = tabClasseZ; tabInterfaceX = tabInterfaceZ; tabObjets = tabInterfaceX; } }
Toutes ces conversions sont acceptées par le
compilateur, mais à l'exécution peuvent être
refusées après vérification et provoquer le
déclenchement d'une exception
ClassCastException,
Cette exception est déclenchée si la
référence désignant un objet de classe
ClasseX (celle utilisée à sa
création par l'instruction new ClasseX ()) n'est
pas convertie dans la classe ClasseX ou dans une classe
ou une interface dans laquelle une référence de
ClasseX est convertible implicitement.
Voici des exemples de cast explicite entre
références (attention, les classes ne sont pas les
mêmes que dans l'exemple des conversions implicites) :
interface InterfaceX { } interface InterfaceZ extends InterfaceX { } class ClasseX { } class ClasseY extends ClasseX implements InterfaceX { } class ClasseZ implements InterfaceZ { } class UneClasse { void uneMethode () { // Création d'un objet de chaque classe Object objet1 = new ClasseX (); // cast implicite de ClasseX vers Object ClasseX objet2 = new ClasseY (); // cast implicite de ClasseY vers ClasseX Object objet3 = new ClasseZ (); // cast implicite de ClasseZ vers Object // Création de tableaux d'un élément Object tab1 = new ClasseX [1]; // Cast explicite sur les objets ClasseY objetY = (ClasseY)objet2; // 1. cast de ClasseX vers ClasseY ClasseX objetX = (ClasseX)objet1; // cast de Object vers ClasseX objetY = (ClasseY)objet1; // cast de Object vers ClasseY // acceptée à la compilation mais déclenche // une exception ClassCastException à l'exécution // car objet1 ne désigne pas un objet de classe ClasseY InterfaceX objet InterfaceX = (InterfaceX)objet2; // 2. cast de ClasseX vers InterfaceX objet InterfaceX = (InterfaceX)objet1; // cast de Object vers InterfaceX // acceptée à la compilation mais déclenche // une exception ClassCastException à // l'exécution car objet1 désigne un objet // de classe ClasseX et classeX n'implémente // pas InterfaceX InterfaceZ objetInterfaceZ = (InterfaceZ)objet3; // 3. cast de Object vers InterfaceZ ClasseX [ ] tabObjetsX = (ClasseX [ ])tab1; // cast de Object vers ClasseX [ ] tabObjetsX = (ClasseX [ ])objet1; // cast de Object vers ClasseX [] acceptée // à la compilation mais déclenche une // exception ClassCastException à // l'exécution car objet1 ne désigne pas // un objet de classe ClasseX [ ] ClasseZ objetZ = (ClasseZ)objetInterfaceZ; // 4. cast de InterfaceZ vers ClasseZ objet InterfaceX = objetZ; // cast implicite de ClasseZ vers InterfaceX objetInterfaceZ = (InterfaceZ)objet InterfaceX; // 5. cast de InterfaceX vers InterfaceZ } }
La figure suivante résume les conversions les plus usuelles que vous serez amené à utiliser :
|
Les casts s'opèrent sur les références et non sur les objets. Dans les deux lignes suivantes : Classe2 objetClasse2 = new Classe2 (); Classe1 objetClasse1 = (Classe1)objetClasse2; objetClasse1 et objetClasse2 ne sont
que des références sur un objet de classe
Classe2. |
|
Les casts automatiques entre types primitifs sont moins nombreux en Java. Aussitôt qu'il y a perte de précision possible (float en int, long en byte, par exemple), vous devez faire un cast explicite. |
|
Les casts d'une référence d'un type de
classe dans un autre ne sont possibles que si la classe
de destination a un lien direct d'héritage avec la
classe de l'objet casté. Si la classe de
conversion n'est pas la classe réelle de l'objet
ou une de ses super classes, une exception
ClassCastException
est déclenchée. |
|
Java ne permet pas de surcharger les opérateurs de cast, mais vous pouvez éventuellement utiliser les constructeurs pour compenser cette absence : pour convertir un objet de classe Classe1 en Classe2, vous créer un constructeur dans la classe Classe2 prenant en paramètre une référence de classe Classe1 : class Classe2 { public Classe2 (Classe1 objetACaster) { // Retranscrire les champs de Classe1 // en ceux de Classe2 } } Il vous suffit de créer un objet de
Classe2 ainsi: Classe2 objetCaste = new
Classe2 (objetDeClasse1);. |
Tableau de précédence (priorité) des opérateurs. Les opérateurs sur une même ligne ont la même priorité, et sont rangés ligne par ligne du plus prioritaire au moins prioritaire.
OPERATEUR ASSOCIATIVITE DESCRIPTION . ( ) [ ] new
de gauche à droite
Opérateurs primaires
! ~ ++ -- + - (cast)
de droite à gauche
Opérateurs unaires
* / %
de gauche à droite
Multiplication, division, reste
+ -
de gauche à droite
Addition, soustraction
<< >> >>>
de gauche à droite
Décalages
< <= > >= instanceof
de gauche à droite
Comparaisons
== !=
de gauche à droite
Egalité, différence
&
de gauche à droite
Opérateur ET bit à bit
^
de gauche à droite
Opérateur OU EXCLUSIF bit à bit
|
de gauche à droite
Opérateur OU bit à bit
&&
de gauche à droite
Opérateur ET
||
de gauche à droite
Opérateur OU
? :
de gauche à droite
Condition
= *= /= %= += -= <<= >>= >>>= &= ^= |=
de droite à gauche
Affectation
L'opérateur new a la même priorité que l'opérateur . , ce qui permet d'écrire directement new Classe1 ().methode1 (). En C++, il faut écrire (new Classe1 ())->methode1 ().
|