3.    Les expressions et les opérateurs


3.1    Type de résultat, transtypage.

Tous les opérateurs calculent un résultat. Quand les opérandes dans un operateur sont  de même type,  le type du résultat coïncide avec le type des opérandes (exception:le type du résultat intégral moins de 4 bytes - par exemple  byte, short  est toujours converti vers type int)

Si les  opérandes n'ont pas le même type, avant l'exécution on fait une conversion de type automatique d'un des opérandes vers le type de l'autre (si c'est possible). Si cette conversion n'est pas possible une faute de compilation est générée. Cette conversion de type porte le nom transtypage implicite (ou automatique).

Le transtypage automatique  peut s'appliquer quand il n'y a pas de risque de perte d’information. La perte de précision est acceptable.

Les six flèches noires dans la figure 3.1 indiquent les conversions sans perte d'information. Les trois flèches pointillées indiquent celles pouvant souffrir  d'une perte de précision

            Fig. 3.1. Transtypage automatique autorisé en Java

public class Oper {
    public static void main(String arg[]){
        int k,i1=4, i2 = 5;
        double f1 =5.0,f2=5.0;
        f1= i1/i2 +i1/f2;
    }
}
•    L'expression i1/i2 se fait sans transtypage est le résultat est 0
•    Dans l'expression i1/f2, i1 est convertis en double est le résultat est 0.8
•    Le résultat final est de type double

En Java, une variable de type caractère peut intervenir dans une expression arithmétique car il est prévu une promotion numérique de char en int. En effet, une valeur de type caractère peut être considérée de deux façons :
•    comme le caractère concerné : a, Z, fin de ligne...,
•    comme le code de ce caractère, c’est-à-dire une séquence de 16 bits; or à ce dernier on peut toujours faire correspondre un nombre entier
Dans l'expression 'z' - 'w' (qui vaut 3) pour faire la soustraction, d'abord les deux caractères sont convertis automatiquement en int. La conversion de char (16 bits) en int (32 bits) se fait en complétant le motif binaire initial par 16 bits à zéro.
Parmi les conversions légales, on trouve celle de char en int, mais pas celle de char en short. En effet, bien que ces deux types soient de même taille, le type short est destiné à des nombres avec signe, tandis que, par convention, la conversion d’une valeur de type char doit toujours fournir un résultat positif. Le type short s’avère alors trop petit pour accueillir toutes les valeurs possibles.


Dans langage Java il y a encore une autre possibilité. Quand le transtypage automatique n'est pas possible, on peut utiliser transtypage explicite (caste). Ente parenthèses est introduit le type dans lequel il faut faire la conversion.
int i = (int)56.4;            byte sh = (byte)456;

On force alors le type, indiquant ainsi au compilateur qu’on est conscient du problème de risque de perte d’information. C’est aussi le cas lorsqu’on veut prendre la partie entière d’un réel.

public class Oper {
    public static void main(String arg[]){
        int in;
        long ln=123L;
        double db = 34.5;
        in = ln;        //type mismatch
        in = db;        //type mismatch
        in = (int)ln;    //ok
        in = (int)db;    //ok
    }
}

Dans les expressions arithmétiques, les entiers de type byte ou short sont considérés comme des int. Si b1, b2 et b3 sont de type byte, b2 + b3 est considéré être un int. L’affectation b1 = b2 + b3; et b1 = 130; sont  illégales alors que b1 = 5 est acceptée.

public class Oper {
    public static void main(String arg[]){
        byte b1,b2=2,b3=3;
        b1 = 5;                //ok!
        b1 = b2+b3;            // type mismatch
        b1 = (byte)(b2+b3);    //ok!
        b1=130;                //type mismatch
        b1=(byte)130;        //ok!
    }
}


3.2    La priorité et l'associativité des opérateurs

Lorsque plusieurs opérateurs apparaissent dans une même expression, il est nécessaire de savoir dans quel ordre ils seront utilisés. Cet ordre est déterminé par la priorité relative associée à chaque opérateur. En cas de priorités identiques, les calculs dans la pluparts des operateurs s’effectuent de gauche à droite. On dit que l’on a affaire à une associativité de gauche à droite. Il y a quelques operateurs qui ont l'associativité de droite à gauche.

Le tableau suivant fournit la liste complète des opérateurs de Java, classés par ordre de priorité décroissante et accompagnés de leur mode d’associativité (––> signifiant de gauche à droite et <–– de droite à gauche).

Opérateurs

Associativité

()   []   .   ++(postfixé)   --(postfixé)

––>

+(unaire)   -(unaire)   ++(préfixé)   --(préfixé)   ~   !  cast new

<––

*   /  %

––>

+  -

––>

<<    >>    >>>

––>

<    <=    >    >=    instanceof

––>

==    !=

––>

&

––>

^

––>

|

––>

&&

––>

||

––>

?:

––>

=  +=  -=  *=  /=  %=  <<=  >>=  >>>=  &=  |=  ^=

<––


Tab. 3.1. Les opérateurs de Java et leurs priorités

Notez qu’en Java, un certain nombre de notations se trouvent considérées comme des opérateurs et, en tant que tels, soumis à des règles de priorités:
– des références à des éléments d’un tableau : opérateur [],
– des références à un champ ou à une méthode d’un objet : opérateur .,
– des appels de méthodes : opérateur ().



3.3    Les opérateurs arithmétiques
•    +    addition;
•    -    soustraction,
•    *    multiplication,
•    /    division.

De plus, il existe un opérateur de modulo noté % qui peut porter sur des entiers ou sur des flottants. Avec des entiers, il fournit le reste de la division entière de son premier opérande par son second. Avec des flottants il fonction de la même manière mais les résultats sont approximatifs.

public class Oper {
    public static void main(String arg[]){
        System.out.println("11%4:\t"+11%4);
        System.out.println("23%6:\t"+23%6);
        System.out.println("-11%3:\t "+ -11%3);
        System.out.println("12.5%3.5:\t "+12.5%3.5);
        System.out.println("-15.2%7.5:\t "+-15.2%7.5);
    }
}

•    Dans le cas des entiers, comme a été déjà signalé, le dépassement de capacité n’est jamais détecté. On se contente de conserver les bits les moins significatifs du résultat.
•    Dans le cas des entiers, la division par zéro (par / ou par %) conduit à une erreur d’exécution.
•    En Java, aucune opération sur les flottants ne conduit à un arrêt de l’exécution (pas même une division par zéro !). En effet, les flottants sont codés en respectant les conventions IEEE 754. Celles-ci imposent qu’il existe une valeur particulière pour représenter l’infini positif, l’infini négatif et une valeur non calculable (NaN- not a number).



public class Oper {
    public static void main(String arg[]){
        int i= 20000000;
        System.out.println ("overloading: "+i*i);  //dépassement de capacité
        float x = 1e30f ;
        float y ;
        y = x*x ;
        System.out.println (x + " a pour carre : " + y) ;
        float zero = 0.f ; // division flottante par zero
        y=4.5f;
        float z = y/zero ;
        System.out.println (y + " divise par 0 = " + z) ;
        System.out.println("l'oppose de " + y + " = "+ -y);
        float z1 = z/y ;
        System.out.println (z + "/" + y + " = " + z1) ;
        System.out.println ("0./0.:"+zero/zero);
    }
}

 


opérateurs d’incrémentation (++ et --)

Les opérateurs d’incrémentation (++ et --) de variables (entières ou réelles) sont des operateurs à un seul opérande. Les deux opérateurs sont les seules en Java (avec l'affectation) qui ont un effet secondaire.
•    Ils modifient la valeur de l'opérande en ajoutant (dans le cas de ++) ou retranchant (dans le cas de --) valeur 1. L'opérande doit être une variable pas une valeur constante. Si non le compilateur affiche une erreur.
•    Ils calculent un résultat. La valeur de résultat est la valeur ancienne de l'opérande (avant l’incrémentation) dans le cas où l’opérateur suit la variable (j++) ; elle est la nouvelle valeur (après l’incrémentation) dans le cas contraire (++j).


public class Oper {
    public static void main(String arg[]){
        int k, m=3;
        k = m++;    //m vaut 4,  k vaut 3 - valeur ancienne
        m=3;
        k = ++m;    //m vaut 4,    k vaut 4 - valeur novelle
        m=3;
        k = m--;    //m vaut 2,  k vaut 3 - valeur ancienne
        m=3;
        k = --m;    //m vaut 2,    k vaut 2 - valeur novelle   
        k= (m++)++;    //erreur!     m++ n'est pas constante
    }
}

Les priorités élevées de ces opérateurs unaires permettent d’écrire des expressions assez compliquées sans qu’il soit nécessaire d’employer des parenthèses pour isoler leur opérande.
Utilisation des opérateurs d’incrémentation évite les conversions. Si bt est du type  byte pour incrémenter sa valeur on a le choix entre bt++;  et  bt = (byte)(bt+1); Le dernier fait deux conversions supplémentaires.



L’opérateur d’affectation (=)

L’opérateur d’affectation (=) est un opérateur binaire qui a aussi un effet secondaire.
•    Il réalise une action – affecte la valeur d'opérande à droite à l'opérande à gauche. L'opérande à gauche doit être une variable pas une valeur constante. Si non le compilateur affiche une erreur.
•    Il calcule un résultat qui peut être utilise dans un calcul suivant. La valeur de résultat est la valeur affectée.

Cet opérateur d’affectation peut faire intervenir d’autres expressions, comme dans c=b+3; La faible priorité de cet opérateur (elle est inférieure à celle de tous les opérateurs arithmétiques et de comparaison) fait qu’il y a d’abord évaluation de l’expression b + 3. La valeur ainsi obtenue est ensuite affectée à c.

•    L'opérateur d’affectation possède une associativité de droite à gauche. C’est ce qui permet à une expression telle que : i = j = 5; d’évaluer d’abord l’expression j = 5 avant d’en affecter la valeur (5) à la variable j. Bien entendu, la valeur finale de cette expression est celle de i après affectation, c’est-à-dire 5.

public class Oper {
    public static void main(String arg[]){
        int k, m=3,z=4,p;
        p = k =(m = 2) *(z = 4);  
                                     //m vaut 2
                                    //z vaut 4
                                    //k vaut 8
                                    //p vaut 8
        p = k = 2;                    //k vaut 2,  p vaut 2
        p = (k = 2);                //la même chose
        (p = k) = 2;                //erreur – l'opérande de gauche (p=k) est constante
    }
}

L'opérateur "=" couplé avec un opérateur arithmétique permet de retourner dans l'opérande de gauche l'opération arithmétique de celui-ci avec l'opérande de droite. Ainsi c+=3; équivaut à c=c+3;

3.4    Les opérateurs de comparaisons logique

•    ==    Rend true si les deux opérandes sont égaux.
•    !=    Rend true si les deux opérandes sont différents.
•    <    Rend true si l'opérande de gauche est strictement inférieur à l'opérande de droite.
•    <=    Rend true si l'opérande de gauche est inférieur ou égal à l'opérande de droite.
•    >    Rend true si l'opérande de gauche est strictement supérieur à l'opérande de droite.
•    >=    Rend true si l'opérande de gauche est supérieur ou égal à l'opérande de droite.

public class Oper {
    public static void main(String arg[]){
        System.out.println("25>11:"+(25>11)+ "\t17<=8: "+(17<=8));
    }
}

3.5    Opérateurs logiques sur des booléens

•    &&    Opérateur ET (AND). Le résultat est true, si les deux opérandes ont la valeur true.
•    ||    Opérateur OU (OR). Le résultat est true, si l'un des deux opérandes a la valeur true.
•    !    Opérateur NON (NOT). Le résultat est true si le seul opérande a la valeur false.

public class Oper {
    public static void main(String arg[]){
        boolean b1=true,b2=true,b3=false,b4=false;
        System.out.println("true && true: "+(b1&&b2)+ "\t true && false: "+(b1&&b3));
        System.out.println("true || false: "+(b1||b3)+ "\t false || false: "+(b3&&b4));
        System.out.println("!true: "+!b1+"\t !false: "+!b3);
    }
}

3.6    Opérateurs logiques bit à bit

•    &    Opérateur ET (AND).
•    |    Opérateur OU (OR).
•    ~    Opérateur NON (NOT).
•    >>    Décalage arithmétique à droite. 
•    >>>    Décalage logique à droite. Le signe de l'opérande de gauche n'est pas gardé. On introduit toujours un zéro à gauche.
•    <<    Décalage à gauche. C'est une multiplication par 2

public class Oper {
    public static void main(String arg[]){
        System.out.println("7&8: "+(7&8)+"\t7|8: "+(7|8));
        System.out.println("~7: "+ (~7));
        System.out.println("7 << 2: "+(7 << 2));
        System.out.println("-7 >> 2: "+(-7 >> 2)+"\t -7 >>> 2: "+(-7 >>> 2));       
    }
}

3.7    Opérateur ternaire

public class Oper {
    public static void main(String arg[]){
        int res,a=4, b=9;
        res = a>b?a:b;
    }
}