我們給出一個對變數x和i的聲明即可,它肯定是一個合法的語句:
但是,它並不是:
許多程式員都會認為該迷題中的第一個運算式(x += i)只是第二個運算式(x = x + i)的簡寫方式。但是這並不十分準確。這兩個運算式都被稱為賦值運算式。第二條語句使用的是簡單賦值操作符(=),而第一條語句使用的是複合賦值操作符。(複合賦值操作符包括 +=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=和|=)Java語言規範中講到,複合賦值 E1 op= E2等價於簡單賦值E1 = (T)((E1)op(E2)),其中T是E1的類型,除非E1隻被計算一次。
換句話說,複合賦值運算式自動地將它們所執行的計算的結果轉型為其左側變數的類型。如果結果的類型與該變數的類型相同,那麼這個轉型不會造成任何影響。然而,如果結果的類型比該變數的類型要寬,那麼複合賦值操作符將悄悄地執行一個窄化原始類型轉換。因此,我們有很好的理由去解釋為什麼在嘗試著執行等價的簡單賦值可能會產生一個編譯錯誤。
為了說得具體一些,並提供一個解決方案給這個謎題,假設我們在該謎題的兩個賦值運算式之前有下面這些聲明:
short x = 0; int i = 123456; |
複合賦值編譯將不會產生任何錯誤:
你可能期望x的值在這條語句執行之後是123,456,但是並非如此l,它的值是-7,616.int類型的數值123456對於short來說太大了。自動產生的轉型悄悄地把int數值的高兩位給截掉了。這也許就不是你想要的了。
相對應的簡單賦值是非法的,因為它試圖將int數值賦值給short變數,它需要一個顯式的轉型:
x = x + i; // 不要編譯——“可能會丟掉精度” |
這應該是明顯的,複合賦值運算式可能是很危險的。為了避免這種令人不快的突襲,請不要將複合賦值操作符作用於byte、short或char類型的變數上。在將複合賦值操作符作用於int類型的變數上時,要確保運算式右側不是long、float或double類型。在將複合賦值操作符作用於float類型的變數上時,要確保運算式右側不是double類型。這些規則足以防止編譯器產生危險的窄化轉型。
總之,複合賦值操作符會悄悄地產生一個轉型。如果計算結果的類型寬於變數的類型,那麼所產生的轉型就是一個危險的窄化轉型。這樣的轉型可能會悄悄地丟棄掉精度或數量值。對語言設計者來說,也許讓複合賦值操作符產生一個不可見的轉型本身就是一個錯誤;對於在複合賦值中的變數類型比計算結果窄的情況,也許應該讓其非法才對。