30 votes

Pourquoi le compilateur ne donne-t-il pas d'erreur pour cette opération d'ajout?

Je sais que le compilateur ne implicite type de conversion pour les littéraux entiers. Par exemple:

byte b = 2; // implicit type conversion, same as byte b = (byte)2;

Le compilateur me donne une erreur si la plage de débordement:

byte b = 150; // error, it says cannot convert from int to byte

Le compilateur donne le même message d'erreur lorsque la variable est passée d'une expression:

byte a = 3;
byte b = 5;
byte c = 2 + 7; // compiles fine
byte d = 1 + b; // error, it says cannot convert from int to byte
byte e = a + b; // error, it says cannot convert from int to byte

Je suis venu à la conclusion que le résultat d'une expression qui fait intervenir des variables ne peut être garantie. La valeur qui en résulte peut être à l'intérieur ou à l'extérieur de la plage d'octets, de sorte compilateur déclenche une erreur.

Ce qui m'intrigue, c'est que le compilateur ne génère pas une erreur quand je l'ai mis comme ceci:

byte a = 127;
byte b = 5;
byte z = (a+=b); // no error, why ?

Pourquoi n'est-il pas me donner une erreur?

22voto

Makoto Points 23751

Pendant la décompilation de votre code d'expliquer ce que Java est en train de faire, la raison pourquoi il le fait, il peut être généralement trouvé dans la spécification du langage. Mais avant d'aller dans ce que, nous allons établir quelques notions importantes:

Donc, on en revient à ce scénario: pourquoi l'ajout de deux octets qui sont bien plus que ce qu'un octet peut gérer produisent pas une erreur de compilation?

Il ne sera pas soulever une exception d'exécution en raison de dépassement de capacité.

C'est le scénario dans lequel deux nombres additionnés soudain produire un très petit nombre. En raison de la petite taille de l' byte'de la gamme s, il est extrêmement facile à débordement; par exemple, l'ajout de 1 à 127 le ferait, résultant dans -128.

Le chef de la raison, il va enrouler autour de est en raison de la façon dont Java gère valeur primitive de conversion; dans ce cas, nous parlons d' un rétrécissement de conversion. C'est-à-dire, même si la somme produite est plus grand que byte, le rétrécissement de la conversion en cause de l'information à être supprimées pour permettre les données pour les adapter en byte, que cette conversion ne jamais provoque une exception d'exécution.

Pour briser votre scénario étape par étape:

  • Java ajoute de l' a = 127 et b = 5 ensemble pour produire 132.
  • Java comprend qu' a et b sont de type byte, de sorte que le résultat doit être de type byte.
  • L'entier résultat de ceci est encore 132, mais à ce point, Java effectuer un cast pour affiner le résultat à l'intérieur d'un octet donnant effectivement vous (byte)(a += b).
  • Maintenant, les deux a et z contenir le résultat -124 en raison de la wrap-around.

5voto

Little Santi Points 1003

Je suis venu à la conclusion que le résultat d'une expression qui fait intervenir des variables ne peut être garantie. La valeur qui en résulte peut être à l'intérieur ou à l'extérieur de la plage d'octets, de sorte compilateur déclenche une erreur.

Non, ce n'est pas la raison. Les compilateurs d'un staticly tapé la langue de travail de cette façon: Toute variable doit être déclarée et dactylographiées, de sorte que même si sa valeur n'est pas connue au moment de la compilation, son type est connu. Il en va de même pour les implicites des constantes. Basé sur ce fait, les règles permettant de calculer les échelles sont essentiellement ces:

  • Toute variable doit être la même ou une plus grande échelle que l'expression de son côté droit.
  • Toute expression a la même échelle de la durée maximale impliqué.
  • Un cast explicite des forces, de la corse, l'échelle de droite de l'expression.

(Ce sont en fait une vue simplifiée; effectivement peut-être un peu plus complexe).

L'appliquer à votre cas:

byte d = 1 + b

La réelle échelles sont:

byte = int + byte

... (parce 1 est considéré comme un accord implicite int constante). Ainsi, l'application de la première règle, la variable doit être d'au moins int de l'échelle.

Et dans ce cas:

byte z = (a+=b);

La réelle échelles sont:

byte = byte += byte

... ce qui est OK.

Mise à jour

Alors, pourquoi byte e = a + b produire une erreur de compilation?

Comme je l'ai dit, le type réel règles en java sont plus complexes: Alors que les règles générales s'appliquent à tous les types primitifs byte et short types sont plus restreintes: Le compilateur suppose que l'ajout/soustrayant deux ou plusieurs octets/short est risquer de provoquer un dépassement de capacité (comme @Makoto dit), donc il faut être stockées en tant que le type suivant dans l'échelle considérée comme "sûre": un int.

5voto

ThanksForAllTheFish Points 2119

La réponse est fournie par JLS 15.26.2:

Par exemple, le code suivant est correct:

short x = 3;

x += 4.6;

et les résultats dans x ayant la valeur 7, parce que c'est équivalent à:

short x = 3;

x = (short)(x + 4.6);

Donc, comme vous pouvez le voir, le dernier cas en fait de travailler en raison de l'affectation d'addition (comme tout autre opérateur d'affectation) effectue un cast implicite de la main gauche type (et dans votre cas a est byte). L'extension, c'est équivalent à byte e = (byte)(a + b);, ce qui permettra de compiler heureusement.

4voto

ajb Points 11197

La raison fondamentale en est que le compilateur se comporte un peu différemment lorsque les constantes sont impliqués. Tous les littéraux entiers sont traités comme int des constantes (sauf s'ils ont un L ou l à la fin). Normalement, vous ne pouvez pas affecter un int d'un byte. Cependant, il y a une règle spéciale où les constantes sont impliqués; voir JLS 5.2. Pour l'essentiel, dans une déclaration telle que celle - byte b = 5;, 5 est int, mais c'est légal de faire de la "rétrécissement" de la conversion à l' byte car 5 est une constante, et parce qu' il s'inscrit dans la gamme d' byte. C'est pourquoi, byte b = 5 qui est permis et byte b = 130 ne l'est pas.

Toutefois, byte z = (a += b); est une autre affaire. a += b ajoute juste b de a, et renvoie la nouvelle valeur de a; cette valeur est affectée à l' a. Depuis a est un octet, il n'y a pas de rétrécissement de conversion impliqués--vous êtes l'affectation d'un octet un octet. (Si a ont un int, le programme serait toujours illégal.)

Et les règles disent que a + b (et, par conséquent, a = a + bou a += b) ne débordera pas. Si le résultat, au moment de l'exécution, est trop grand pour un octet, les bits de poids se perdent--la valeur s'enroule autour de. En outre, le compilateur ne sera pas "suivre la valeur de l'avis qu' a + b serait supérieur à 127; même si nous pouvons dire que la valeur est supérieure à 127, le compilateur ne pas garder une trace des valeurs précédentes. Aussi loin qu'il sait, quand il voit a += b, il sait seulement que le programme va ajouter b de a lorsqu'il s'exécute, et il n'a pas l'air lors de précédentes déclarations pour voir ce que les valeurs seront. (Une bonne optimisation du compilateur pourrait en fait faire ce genre de travail. Mais nous parlons de ce qui rend un programme légal ou pas, et les règles de la "légalité" de ne pas se préoccuper de l'optimisation.)

4voto

Pooya Points 3360

J'ai rencontré cette avant dans un projet, et c'est ce que j'ai appris:

contrairement au c/c++, Java est toujours utiliser signé primitives. Un octet est de -128 à +127 donc, si vous assigner n'importe quoi derrière cette plage, il vous donnera erreur de compilation.

Si vous convertissez explicitement à l'octet comme (byte) 150 encore vous ne obtenez ce que vous voulez (vous pouvez vérifier avec le débogueur et le voir se convertir à autre chose).

Lorsque vous utilisez des variables comme l' x = a + b , car le compilateur ne connaît pas les valeurs au moment de l'exécution et ne peut pas calculer si -128 <= a+b <= +127 il va donner à l'erreur.

Concernant votre question sur pourquoi le compilateur ne donne pas d'erreur sur quelque chose comme a+=b :

Je creuse dans les compilateur java disponible à partir de openjdk à

http://hg.openjdk.java.net/jdk9/jdk9/langtools.

J'ai tracé l'arbre de traitement des opérandes et est venu à une intéressante expression dans l'un des compilateur de fichiers Lower.java partiellement responsable pour traverser le compilateur de l'arbre. voici une partie du code qui serait intéressant (Assignop est pour tous les opérandes comme += -= /= ...)

public void visitAssignop(final JCAssignOp tree) {
                        ...
                        Symbol newOperator = operators.resolveBinary(tree,
                                                                      newTag,
                                                                      tree.type,
                                                                      tree.rhs.type);
                        JCExpression expr = lhs;
                        //Interesting part:
                        if (expr.type != tree.type)
                            expr = make.TypeCast(tree.type, expr);
                        JCBinary opResult = make.Binary(newTag, expr, tree.rhs);
                        opResult.operator = newOperator;:

                        ....

comme vous pouvez le voir si l' rhs a différents type de l' lhs, le type de fonte aurait lieu de sorte que même si vous déclarez float ou double sur le côté droit, (a+=2.55) vous n'aurez pas de message d'erreur, car le type de fonte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X