2015-09-09 46 views
112

最近我在閱讀Spring Framework的源代碼。我無法理解的東西在這裏:Java三元運算符vs if/else <JDK8兼容性

public Member getMember() { 
    // NOTE: no ternary expression to retain JDK <8 compatibility even when using 
    // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable 
    // as common type, with that new base class not available on older JDKs) 
    if (this.method != null) { 
     return this.method; 
    } 
    else { 
     return this.constructor; 
    } 
} 

此方法是類org.springframework.core.MethodParameter的成員。代碼很容易理解,而評論很難。

注:使用JDK 8編譯器8兼容性即使當(潛在選擇java.lang.reflect.Executable如常見的類型,與該新的基類在較舊的JDK不可用)

什麼沒有三元表達保留JDK <在這種情況下使用三元表達式和使用if...else...構造之間的區別?

回答

102

當你想到的操作數的類型,這個問題就更加明顯了:

this.method != null ? this.method : this.constructor 

具有類型兩個操作數的最專業的常見類型,即最專業型通用於this.methodthis.constructor

在Java 7中,這是java.lang.reflect.Member,但是Java 8類庫引入了一種新類型java.lang.reflect.Executable,它比通用Member更專用。因此,對於Java 8類庫,三元表達式的結果類型是Executable而不是Member

編譯三元運算符時,Java 8編譯器的一些(預發佈)版本似乎在生成的代碼中生成了對Executable的明確引用。此將觸發一類的負載,並且因此又一個ClassNotFoundException在運行時用一個類庫< JDK 8運行時,因爲只Executable存在JDK≥8.

如Tagir Valeev在this answer指出的,這實際上是一個是在JDK 8的預發佈版本中存在缺陷並且已經被修復,所以if-else解決方法和解釋性評論現在已經過時。

附加說明:人們可能會得出這樣的結論,這個編譯器故障而存在的Java 8之前,然而,對於由OpenJDK 7的三元生成的字節碼是一樣的通過OpenJDK的8生成在字節碼事實上,表達式的類型在運行時完全沒有提到,代碼實際上只是測試,分支,加載,返回而沒有進行任何額外的檢查。所以請放心,這不是問題(已經),而且在Java 8的開發過程中似乎確實是暫時的問題。

+1

那麼在JDK 1.7上如何使用JDK 1.8編譯代碼。我知道使用較低版本的JDK編譯的代碼可以在較高版本的JDK上運行,而不會造成麻煩。反之亦然? – jddxf

+1

@jddxf只要您指定了適當的類文件版本,並且不使用任何在更高版本中不可用的功能,一切都很好。問題必然會發生,但是如果這種使用在這種情況下隱含地發生。 – dhke

+13

@jddxf,使用-source/-target javac選項 –

7

的主要區別是,一個ifelse塊是語句而三元(更通常被稱爲Java中的條件操作員)是表達

A 陳述可以對某些控制路徑上的調用者執行諸如return之類的操作。一個表達可以在分配中使用:

int n = condition ? 3 : 2;

所以兩個表達式在三元狀態後必須coercable到相同的類型。這可能會在Java中引起一些奇怪的效果,特別是在自動裝箱和自動引用投射方面 - 這就是您的發佈代碼中的評論所指的。在你的情況下,表達式的強制是java.lang.reflect.Executable類型(因爲這是最專業的類型),並且在舊版本的Java中不存在。

在風格上,如果代碼類似於語句,則應該使用ifelse塊;如果類似表達式,則應使用三元。

當然,如果您使用lambda函數,則可以使ifelse塊的行爲與表達式類似。

6

在三元表達式返回值類型是由父類,如Java的8

很難說明爲什麼鑄造不能被寫入該改變的影響。

30

這是在2013年5月3日的quite old commit中引入的,距離官方JDK-8近一年的時間發佈。編譯器在那個時候處於繁重的開發中,所以可能會出現這種兼容性問題。我想,Spring團隊只是測試了JDK-8構建,並試圖解決問題,即使它們實際上是編譯器問題。通過JDK-8官方發佈,這變得無關緊要。現在,此代碼中的三元運算符按預期正常工作(在編譯的.class文件中沒有提及Executable類)。

目前類似的情況出現在JDK-9中:在JDK-8中可以很好編譯的某些代碼在JDK-9 javac中失敗。我想,這些問題大部分都會在發佈之前得到修復。

+2

+1。那麼,這是早期編譯器中的一個錯誤嗎?這種行爲是否違背了規範的某些方面?它涉及到「可執行文件」嗎?還是僅僅是因爲甲骨文意識到,他們可以改變這種行爲,仍然符合規範並且不會破壞後向兼容性? – ruakh

+2

@ruakh,我想這是錯誤。在字節碼中(無論是在Java-8還是之前的版本中),完全沒有必要明確地轉換爲「可執行」類型。在Java-8中,表達式類型推斷的概念發生了巨大變化,並且這部分被完全重寫,因此早期實現有缺陷並不令人感到意外。 –