Pourquoi l'affectation de composé dans Java ne capte-t-elle pas des problèmes de débordement?

StackOverflow https://stackoverflow.com/questions/5480873

Question

À mon choc, il s'avère que le code suivant se compilera sans même avertissements:

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value += increment;
}

Alors que cela donne une erreur de compilation, comme vous vous en doutez:

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value = value + increment;
}

Je l'ai vérifié et, en effet, le JLS (section 15.26.2) a ceci à dire:

Une expression d'attribution composée de la forme e1 op = e2 est équivalente à e1 = (t) ((e1) op (e2)), où t est le type de e1, sauf que E1 n'est évalué qu'une seule fois.

Cela me semble ridicule. Pourquoi ont-ils ressenti le besoin de lancer explicitement ici? Il semble que la conversion de type automatique aurait géré l'élargissement de toute façon, et se rétrécir automatiquement comme cela est à peu près garanti pour entraîner un débordement entier.

Était-ce utile?

La solution

Ici est une explication:

Lorsque vous effectuez une affectation (le premier extrait de code), Java applique la vérification du type car le LHS et le RHS peuvent très bien être indépendants les uns des autres.

Mais l'opérateur composé ressemble plus à un opérateur incrémentiel. Le + = modifie la valeur de la variable impliquée, plutôt que d'attribuer une nouvelle valeur à la variable. Lorsque vous modifiez un octet, vous vous attendez à un octet en conséquence. Pour faciliter la vie, Java fait une conversion de type implicite pour les composés-opérateurs car ce sont des modificateurs.

Autres conseils

Les opérateurs d'affectation composés sont spécifiés par le JLS (15.26.2) comme suit:

"Une expression d'attribution composée de la forme E1 op= E2 est équivalent à

      E1 = (T)((E1) op (E2))`, 

où t est le type d'E1, sauf que E1 n'est évalué qu'une seule fois. "

Dans ce cas, E1 est de type int E2 est de type long, et OP est +. Cela équivaut donc à:

value = (int)(value + increment);

L'ajout d'un int et un long donne un long qui est ensuite rejeté à un int avant affectation. Tout est bien, donc aucune erreur de compilation.

La différence entre cela et la mission simple (c'est-à-dire value = value + increment;), c'est que l'affectation simple n'a pas de typaste.


D'accord alors Pourquoi l'ont-ils définie de cette façon?

Je pense que la raison est de faire des exemples comme celui-ci:

    byte b = ...
    b += 1;

Sans le typast, b += 1 serait une erreur de compilation et vous auriez besoin de l'écrire sous le nom de:

    b += (byte) 1;

Ce lien a analysé le problème que vous avez soulevé.

Comportement variable pour une perte de précision possible

Pour éviter les surprises désagréables, n'utilisez pas les opérateurs d'affectation composés sur des variables d'octets de type, court ou char. Lorsque vous utilisez des opérateurs d'affectation composés sur des variables de type int, assurez-vous que l'expression du côté droit n'est pas de type long, flottant ou double. Lorsque vous utilisez des opérateurs d'affectation composés sur des variables de type float, assurez-vous que l'expression du côté droit n'est pas de type double. Ces règles sont suffisantes pour empêcher le compilateur de générer des moulages de rétrécissement dangereux.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top