天天看點

Dos Equis

這個謎題将測試你對條件操作符的掌握程度,這個操作符有一個更廣為人知的名

字:問号冒号操作符。下面的程式将會列印出什麼呢?  

public class DosEquis{ 

        public static void main(String[] args){ 

                 char x = 'X' ; 

                 int i = 0; 

                 System.out.println(true ? x : 0);  

                 System.out.println(false ? i : x);  

        } 

這個程式由兩個變量聲明和兩個print 語句構成。第一個print 語句計算條件表

達式(true ? x : 0)并列印出結果,這個結果是char類型變量 x 的值’X’。而

第二個print 語句計算表達式(false ? i : x)并列印出結果,這個結果還是依

舊是’X’的x,是以這個程式應該列印 XX。然而,如果你運作該程式,你就會

發現它列印出來的是X88。這種行為看起來挺怪的。第一個print 語句列印的是

X,而第二個列印的卻是88。它們的不同行為說明了什麼呢?  

答案就在規範有關條件表達式部分的一個陰暗的角落裡。請注意在這兩個表達式

中,每一個表達式的第二個和第三個操作數的類型都不相同:x 是 char類型的,

而 0 和 i 都是 int類型的。就像在謎題 5 的解答中提到的,混合類型的計算會引

起 混亂,而這一點比在條件表達式中比在其它任何地方都表現得更 明顯。你可能

考慮過,這個程式中兩個條件表達式的結果類型是相同的,就像它們的操作數類

型是相同的一樣,盡管操作數的順序颠倒了一下,但是實際情況并非如此。  

确定條件表達式結果類型的規則過于冗長和複雜,很難完全記住它們,但是其核

心就是一下三點:  

   *   如果第二個和第三 個操作數具有相同的類型,那麼它就是條件表達式的類

       型。換句話說,你可以通過繞過混合類型的計算來避免大麻煩 。  

   *   如果一個操作數的類型是 T,T 表示 byte、short 或 char,而另 一個操作

       數是一個 int 類型的常量表達式,它的值是可以用類型 T 表示的,那麼條

       件表達式的類型就是 T。  

   *   否則,将對操作數類型運用二進制數字提升,而條件表達式的類型就是第

       二個和第三個操作數被提升之後的類型。 

2、3 兩點對本謎題是關鍵。在程式的兩個條件表達式中,一個操作數的類型是

char,另一個的類型是 int。在兩個表達式中,int 操作數都是0,它可以被表

示成一個 char。然而,隻有第一個表達式中的 int 操作數是常量  (0),而第二

個表達式中的 int 操作數是變量               (i)。是以,第 2 點被應用到了第 一個表達式

上,它傳回的類型是 char,而第 3 點被應用到了第二個表達式上,其傳回的類

型是對 int 和 char 運用了二進制數字提升之後的類型,即 int。  

條件表達式的類型将确定哪一個重載的print 方法将被調用。對第一個表達式來

說,PrintStream.print(char)将被調用,而對第二個表達式來說,

PrintStream.print(int)将被調用。前一個重載方法将變量 x 的值作為Unicode

字元 (X)來列印,而後一個重載方法将其作為一個十進制整數 (88)來列印。

至此,謎題被解開了。  

總之,通常最好是在條件表達式中使用類型相同的第二和第三操作數。否則,你

和你的程式的讀者必須要徹底了解這些表達式行為的複雜規範。  

對語言設計者來說,也許可以設計一個犧牲掉了部分靈活性,但是增加了簡潔性

的條件操作符。例如,要求第二和第三操作數必須就有相同的類型,這看起來就

很合理。或者,條件操作符可以被定義為對常量沒有任何特殊處理。為了讓這些

選擇對程式員來說更加容易接受,可以提供用來表示所有原始類型字面常量的語

法。這也許确實是一個好注意,因為它增加了語言的一緻性和完備性,同時又減

少了對轉型的需求。