簡體   English   中英

為什么Java中的復合賦值沒有捕獲溢出問題?

[英]Why doesn't compound assignment in Java catch overflow problems?

令我震驚的是,以下代碼將在沒有警告的情況下編譯:

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

雖然這會產生編譯時錯誤,正如您所期望的那樣:

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

我檢查了一下,事實上,JLS(第15.26.2節)有這樣的說法:

形式E1 op = E2的復合賦值表達式等效於E1 =(T)((E1)op(E2)),其中T是E1的類型,除了E1僅被評估一次。

這對我來說似乎很荒謬。 為什么他們覺得有必要在這里明確投射? 似乎自動類型轉換無論如何都會處理擴展,並且像這樣自動縮小幾乎可以保證導致整數溢出。

是一個解釋:

當你執行一個賦值(第一個代碼片段)時,java強制執行類型檢查,因為LHS和RHS很可能彼此獨立。

但復合運算符更像是增量運算符。 + =修改所涉及變量的值,而不是為變量賦值。 修改字節時,您希望得到一個字節作為結果。 為了簡化生活,java為復合運算符執行隱式類型轉換,因為它們是修飾符。

復合賦值運算符由JLS( 15.26.2 )指定如下:

E1 op= E2形式的復合賦值表達式相當於

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

其中T是E1的類型,但E1僅被評估一次。“

在這種情況下,E1的類型為int E2的類型為long ,op為+ 所以這相當於:

value = (int)(value + increment);

添加一個int和一個long會給出一個long ,然后在賦值之前將其int轉換為int 這一切都很好,因此沒有編譯錯誤。

這與簡單賦值(即value = value + increment; )之間的區別在於簡單賦值沒有類型轉換。


好的, 為什么他們這樣定義呢?

我認為原因是做這樣的例子:

    byte b = ...
    b += 1;

如果沒有類型轉換, b += 1將是編譯錯誤,您需要將其寫為:

    b += (byte) 1;

此鏈接分析了您提出的問題。

不同的行為可能會導致精度下降

為避免令人不快的意外,請不要對byte,short或char類型的變量使用復合賦值運算符。 在int類型的變量上使用復合賦值運算符時,請確保右側的表達式不是long,float或double類型。 在float類型的變量上使用復合賦值運算符時,請確保右側的表達式不是double類型。 這些規則足以防止編譯器生成危險的縮小轉換。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM