Comportamento variabile per ansible perdita di precisione

In Java, quando lo fai

int b = 0; b = b + 1.0; 

Si ottiene una ansible perdita di errore di precisione. Ma perché è così, se lo fai

 int b = 0; b += 1.0; 

Non c’è nessun errore?

Questo perché b += 1.0; è equivalente a b = (int) ((b) + (1.0)); . La restringente conversione primitiva (JLS 5.1.3) è nascosta nell’operazione di assegnazione composta.

JLS 15.26.2 Compound Assignment Operators (JLS Third Edition):

Un’espressione di assegnazione composta della forma E1 op = E2 è equivalente a E1 = (T) ((E1) op (E2)) , dove T è il tipo di E1 , tranne che E1 viene valutato solo una volta.

Ad esempio, il seguente codice è corretto:

 short x = 3; x += 4.6; 

e ha come risultato che x ha il valore 7 perché è equivalente a:

 short x = 3; x = (short)(x + 4.6); 

Questo spiega anche perché compaia il seguente codice:

 byte b = 1; int x = 5; b += x; // compiles fine! 

Ma questo non:

 byte b = 1; int x = 5; b = b + x; // DOESN'T COMPILE! 

È necessario eseguire il cast esplicito in questo caso:

 byte b = 1; int x = 5; b = (byte) (b + x); // now it compiles fine! 

Vale la pena notare che il cast implicito nei compiti composti è object di Puzzle 9: Tweedledum del meraviglioso libro Java Puzzlers . Ecco alcuni estratti dal libro (leggermente modificati per brevità):

Molti programmatori pensano che x += i; è semplicemente una scorciatoia per x = x + i; . Questo non è del tutto vero: se il tipo di risultato è più ampio di quello della variabile, l’operatore di assegnazione composto esegue una conversione primitiva a restringimento silenzioso.

Per evitare spiacevoli sorprese, non utilizzare operatori di assegnazione composti su variabili di tipo byte , short o char . Quando si utilizzano operatori di assegnazione composti su variabili di tipo int , assicurarsi che l’espressione sul lato destro non sia di tipo long , float o double . Quando si utilizzano operatori di assegnazione composti su variabili di tipo float , assicurarsi che l’espressione sul lato destro non sia di tipo double . Queste regole sono sufficienti per evitare che il compilatore generi pericolosi calchi di restringimento.

Per i progettisti di linguaggi, è probabilmente un errore per gli operatori di assegnazione compositi generare conversioni invisibili; le assegnazioni composte in cui la variabile ha un tipo più ristretto rispetto al risultato del calcolo dovrebbero probabilmente essere illegali.

L’ultimo paragrafo è degno di nota: C # è molto più severo in questo senso (vedi C # Language Specification 7.13.2 Compound assignment ).