1. 程式人生 > >謎題8:Dos Equis

謎題8:Dos Equis

print 二進制 第一個 復雜 操作 表示 print語句 sys 一個表

這個謎題將測試你對條件操作符的掌握程度,這個操作符有一個更廣為人知的名字:問號冒號操作符。下面的程序將會打印出什麽呢?


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的解答中提到的,混合類型的計算會引起混亂,而這一點比在條件表達式中比在其它任何地方都表現得更明顯。你可能考慮過,這個程序中兩個條件表達式的結果類型是相同的,就像它們的操作數類型是相同的一樣,盡管操作數的順序顛倒了一下,但是實際情況並非如此。

確定條件表達式結果類型的規則過於冗長和復雜,很難完全記住它們,但是其核心就是一下三點:

1,如果第二個和第三個操作數具有相同的類型,那麽它就是條件表達式的類型。換句話說,你可以通過繞過混合類型的計算來避免大麻煩。

2,如果一個操作數的類型是T,T表示byte、short或char,而另一個操作數是一個int類型的常量表達式,它的值是可以用類型T表示的,那麽條件表達式的類型就是T。

3,否則,將對操作數類型運用二進制數字提升,而條件表達式的類型就是第二個和第三個操作數被提升之後的類型。

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)來打印。至此,謎題被解開了。

總之,通常最好是在條件表達式中使用類型相同的第二和第三操作數。否則,你和你的程序的讀者必須要徹底理解這些表達式行為的復雜規範。

對語言設計者來說,也許可以設計一個犧牲掉了部分靈活性,但是增加了簡潔性的條件操作符。例如,要求第二和第三操作數必須就有相同的類型,這看起來就很合理。或者,條件操作符可以被定義為對常量沒有任何特殊處理。為了讓這些選擇對程序員來說更加容易接受,可以提供用來表示所有原始類型字面常量的語法。這也許確實是一個好註意,因為它增加了語言的一致性和完備性,同時又減少了對轉型的需求。

謎題8:Dos Equis