2013-01-08 57 views
11

我一直在閱讀Bloch和Gafter的Java Puzzlers,並進入拼圖10(Tweedledee)。這個難題的實質是如何使E1 + = E2非法,而E1 = E1 + E2合法?

爲變量提供報關單xi這樣,這是一個法律聲明:

x = x + i; 

但這不是:

x += i; 

根據這本書,解決這個問題的方法如下:

Object x = "Buy "; 
String i = "Effective Java!"; 

該書聲稱,在+=運算符中,只有當左邊表達式的類型爲String時,右邊表達式纔可以是任何類型。不過,我試着運行這段代碼,它編譯並運行沒有任何問題。

然後我深入研究了Java語言規範。第15.26.2節討論了兩種情況:左邊的表達式是數組訪問表達式,而不是。如果左邊的操作數表達式不是數組訪問表達式,那麼JLS不會說左邊的表達式是String。當它是,這部分應用:

如果T是一個引用類型,那麼它必須是字符串。因爲class String是最後一個類 ,所以S必須也是String。因此, 複合賦值運算符從不需要簡單賦值運算符所需的運行時檢查 。

❖陣列組件的保存的值和右側 操作數的值被用來執行二進制運算(字符串連接) 通過化合物賦值運算符表示(這是必然 + =)。如果此操作突然完成,則由於同樣的原因,分配表達式 會突然完成並且不會發生分配。

T這裏是在編譯時確定的左側操作數的類型,S是選定的數組組件。所以我想我會改變我的代碼到這一點:

Object[] x = {new Object()}; 
String i = "Effective Java!"; 
x[0] += i; 

但即使是這種代碼編譯,沒有任何問題,運行即使new Object()甚至不是一個遠程String

這是怎麼發生的?這是否意味着Java編譯器偏離了JLS?還有可能以某種方式解決原始的難題?

+0

你有沒有試過創建一個覆蓋'toString'打印到stdout/stderr的對象? – 2013-01-08 15:35:52

+0

你確定應該編譯哪一個?或者,也許這本書有一個錯字。例如,如果x和i是字節,那麼'x + = i;'是有效的,但是'x = x + i;'不是。 –

+0

你確定你確實測試過'x + = i'而不是'i + = x'嗎?我的意思是(String)+ =(Object)很容易編譯,當(Object)+ =(String)不能編譯時。 –

回答

3

試用javac < 1.4.2,它也會在那裏工作。

這是不同版本之間的變化。 爲1.4.2的變化(X + = I;允許之前,不因爲):
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4642850

哪個是正確的,因爲JLS 2版定義:

所有化合物賦值運算符需要兩個操作數都是原始類型, 除了+ =,它允許右操作數是任何類型,如果左手 操作數的類型爲String。

7的變化(X + = I;之前不允許的,因爲允許):
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4741726

哪個是正確的,因爲JLS 3.版(見http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26先前先決條件被移除)


只是一個小小的編輯:我沒有看到任何方式修復/解決Java 7中的難題。0_10

+0

好的,我看到了JLS中的更改,但是執行數組訪問的代碼又如何呢?它不應該適用於最新的JLS,應該嗎? – Malcolm

+0

@Malcom我沒有看到任何矛盾。如果「如果T是引用類型,那麼它必須是字符串」意味着它必須是字符串,或者T的處理方式與字符串相同。我會說,這是第二種選擇,請參閱JLS 5.1.11。字符串轉換。和15.18.1。 ('如果只有一個操作數表達式是String類型的,則在另一個操作數上執行字符串轉換(§5.1.11)以在運行時產生一個字符串.') –

+0

是的,但是如果字符串轉換是自動應用的,爲什麼寫關於字符串的句子呢?此外,轉換髮生在執行二進制操作時,如果操作數是字符串,會發生這種情況。就我個人而言,我認爲這是一個JLS錯誤:他們在段落開始處刪除了有關字符串要求的行,並忘記了這一部分。 – Malcolm

0

在Java 6中,你可以說

Object x = 1; 
String i = "i"; 
x = x + i; // compiles 
x += i; // doesn't compile in Java 6, but does in Java 7. 
System.out.println(x); 

這究竟是爲什麼?

同樣作爲

x[0] = x[0] + i; 

與Java編譯7

Object[] x = {new Object()}; 
String i = "Effective Java!"; 
x[0] += i; 
System.out.println(x[0]); 

打印

[email protected] Java! 

但不與Java 6更新37

Error:Error:line (25)java: src\Main.java:25: incompatible types 
found : java.lang.Object 
required: java.lang.String 

這是否意味着Java編譯器偏離了JLS?

我懷疑這意味着Java 6不會遵循當前的JLS。可能有一個它遵守的舊版本。

而且還有可能以某種方式解決原來的難題?

有一個提示。這將編譯

char ch = '0'; 
ch *= 1.1; 

這不

char ch = '0'; 
ch = ch * 1.1; 
+0

在他的例子中有(Object)+ =(String)。您的建議僅適用於反向:(字符串)+ =(對象)(當對象轉換爲字符串時)。 –

+0

@DmitryZaitsev它可以在Java 7中運行。您嘗試過哪種版本的Java? –

+0

啊,你說得對。我已經使用Java 6. –

2

我有以下的,它顯示給定的溶液,作爲正確答案:

public class testIt 
{ 
    public static void main(String args[]) 
    { 
    new testIt(); 
    } 

    public testIt() 
    { 
    Object x = "Buy"; 
    String i = "Effective Java!" 

    x += i; 

    x = x + i; 
    } 
} 

當我編譯此,我得到

testIt.java: incompatible types 
found:  java.lang.Object 
required: java.lang.String; 

    x += i; 
^
1 error 
+3

您使用的是哪個版本的Java?這編譯並在Java 7上運行。 –

+0

然後Java 7忽略JLS。 – 2013-01-08 15:46:19

+0

但是JLS表示'x + = i'不應該工作嗎?就我所知,JLS只需要在數組中使用'String',在其他情況下它並不重要。 JLS只是說,首先,左邊的操作數被評估爲產生一個變量,然後是左邊的變量的保存值和右邊的操作數的值被用來執行由複合賦值運算符「,與」x + i「沒什麼不同。關於左手錶達是一個字符串,沒有什麼可說的。 – Malcolm

相關問題