與Java SE 4和5增強的設計與實現者探討Java未來
五月底,我在巴黎中心的“Le Grand Rex”參加了令人振奮的“What's Next”會議。兩場基調演講之一的演講者是 Neal Gafter,他是 Java SE 4 和5語言增強的主要設計者和實現者,目前就職于微軟的 .NET 平臺語言團隊。我很幸運的得到代表 InfoQ 采訪他的機會,以下是對話的文字記錄。在他的演講中,Neal 表達了他對 Oracle 收購 Sun 微系統公司的看法:總體來說是件好事,“有人領導下的創新工作會更好地開展下去”,所以在采訪一開始,我便就 Oracle 接手 Java 社區一事詢問了他的觀點。
Neal Gafer:嗯,我想對于 Oracle 的收購來說,社區是最不樂觀的方面了吧。
我想他們還在做很多事情。我是指那些他們一直說在做,但其實還沒做到的事情。他們想用開放的方式來開發語言規范。他們保證過,說至少從現在起 JSR 都會在開放環境下操作,所有專家組的郵件列表都是公開可讀的。
然后他們發起了一些 JSR,例如 Project Coin 和 Progject Lambda。然后就這么進行下去了,他們會提供公開的審查,但是那些專家討論組依然不公開。
我對他們說,只要我能看到那些專家組的討論,我就很樂意為你們提供詳細的反饋作為參考。因為這樣做的話,許多討論的基本方式就會公開無疑,就能在專家組討論的環境里進行理解了。
我得到的答復是:“我們正在努力,到時候會聯系你的”,專家組倒也沒有直接說“我們不想開放”。專家組很樂意開放討論內容,Oracle 也說他們希望開放,但現實就是:還沒這么做。
因此,公開評審階段取消了,到現在還訪問不了。他們正在處理的 JSR 是針對 Java 語言規范的,我很想幫他們進行評審。甚至現在都不太能稱作是專家組了,只是 Alex Buckley 和 Oracle 語言團隊在重新審視語言規范,并修補規范里的錯誤。
Oracle 會說:“這是這版規范已經修復掉的 bug 列表,還有這是修改過的規范”。于是我順著這個列表去網站上復查這些 bug,卻發現它們沒法公開瀏覽。我都不知道哪些 bug 是他們聲稱已經修復掉的,那我又能有什么辦法?
我也得不到足夠的信息。他們說:“啊,我們錯了,它們應該對外公開,我們正在處理此事”。然后公開評審階段就這么取消了,過了一兩個星期他們說:“噢,這些 bug 已經可以看了”,但這已經太晚了,應該在公開評審階段去獲得公開的反饋意見。
當然,他們會很樂意看到在公開評審階段出現內部和外部的反饋意見。公開評審階段是個比較正式的時期,他們也樂意在任何時候接受反饋,所以這其實也只是語言規范方面的一小部分情況而已。但是,他們對于外部環境的重視程度還是不如 Sun 公司以前那么好。他們更看重內部意見。他們有自己的開發人員,他們并不像 Sun 公司那樣樂于接受公司外部人員的貢獻。
作為一種實際舉措,現在他們把大多數工作都放在內部進行,以前是如此,未來可能也一樣。Sun 公司會很熱心地鼓勵社區加入整個過程,這跟 Oracle 很不一樣。我覺得現在他們就像有兩條左腿一樣笨拙,他們應該還在學習吧。
InfoQ:你覺得他們的確在嘗試嗎?
Neal Gafter:我想是的,他們應該不是故意的。在有可能是“無能為力”的情況下,我從來不會把他們當成有什么惡意的想法。我想可能是他們如今要面對的社區和他們之前所接觸的社區有太多區別的緣故吧。
他們正在學習如何與社區協作。雖然起步不盡如人意,但我想他們肯定很想做好。他們一定在不斷努力成為社區良好的協作者。在 Oracle 內部技術方面的問題倒不多,主要還是管理的問題。
假如他們想要發布什么消息,或者想要建立一個公開的郵件列表,他們卻找不到合適的管理途徑。這個公司實在太大了,他們沒法讓人們自力更生。他們必須通過某些流程來批準或頒布某些消息,而現在他們想要解決的這些事情沒法用上之前那些流程了。
所以我覺得他們是想解決問題的,給點時間吧。
目前的情況肯定沒法令人滿意,但我想他們的目標肯定是想成為社區的良好協作者。他們想要創建社區,因為他們從社區里得到的收獲比任何人都要大。
他們的目標如此,我相信他們。前段時間在公開度方面我對他們很嚴厲,但我知道他們在努力。就算他們以后有了進步,我也一樣會繼續督促他們,因為總是會有提高空間的。
至少我認為他們的方向?沒有問題。
InfoQ:我相信下一個問題已經有很多人問過你了,但我對你現在是在為微軟工作這件事還是很感興趣。顯然微軟和 Sun 的關系并不怎么樣,由于各種原因現在可能已經好些了,但某人為 C# 工作的同時,還熱情地參與 Java 的發展實在是很有趣的事情。你是怎么做到的呢?
Neal Gafter:嗯,其實這并不是我的工作,我是指,Java 跟我在微軟的工作沒有任何關系。我參與 Java 發展,只是因為我在乎,我感興趣,而且我的很多朋友也在其中。你知道,我過去很長時間都投身于 Java,我一直在關注它的發展。而且,從微軟身上我也學到了很多有利于 Java 的東西。
許多 Java 在成長過程中會遇到的問題,其實在 C# 那里已經出現過了。許多 Java 目前正在努力嘗試的東西,其實也是 C# 之前想要去做的,C#這么做了,而且做的很不錯。不過 C# 世界也不是盡善盡美的。我的意思是,我們學到了很多東西,這對 Java 來說都是十分寶貴的經驗。
如果要我說哪些東西是我想帶給 Java 世界的話,舉個例子吧,你們有沒有看過 C# 正在構建的異步語言特性?還有 C# 里的 Lambda 表達式?
你要知道,像 C# 和 LINQ 所帶來的并發模型操作等等,我覺得都是對 Java 世界很有幫助的東西。
InfoQ:我猜 C# 有一些優勢,尤其是想要增加一些大功能的時候,C#的用戶會比 Java 小一些,因此可以更輕易地添加一些破壞向后兼容性的功能。還是說,這只是處理問題哲學上的區別?
Neal Gafter:我想只是哲學方面的區別吧。我猜你其實主要是在說泛型。
InfoQ:是的。
Neal Gafter:所以先為讀者明確一下背景吧:在 Java 增加泛型功能時,它們選擇了擦拭法(Erasure)。
換句話說,這種做法不需要對虛擬機進行深度改變,工作都在編譯期間完成了。在運行期間,所有信息,或者說大部分和泛型有關的信息已經從實際的對象上擦除了。你無法在運行時區分一個字符串列表和一個整型列表,它們在運行時的處理方式完全一致。
這么做的原因之一?,是因為我們想直接將現有的類庫泛型化──并且讓類庫的使用者和實現者獨立開來。我們不想強制他們的先后順序,也不想讓 Java 提供商們準備一個“泛型前”的版本和一個“泛型后”的版本。
否則,如果你基于類庫開發的話,那么你必須針對每個版本都構建一份,對吧?理論上說這會十分復雜。實際上,我們對復雜性的害怕程度遠勝于類庫提供商。
當 C# 增加泛型功能時,它們并沒有廢棄舊有的類庫。這些集合類型依然在類庫里。事實上泛型容器的接口和非泛型的接口有繼承關系。泛型的類型會擴展非泛型的那些。
InfoQ:明白。
Neal Gafter:所以,當你在實現一個泛型容器時,你其實也在實現非泛型的容器。這帶來了一定程度上的互操作性。這意味著當你創建了一個泛型的容器之后,可以將其傳遞給需要舊式容器的地方。
反之則不然。假如你有一個舊式的容器,那么你就不能將其交給泛型容器使用。不過其實只要創建一個新的容器來存放所有元素就行了,不會有什么問題。
不過話說回來,我其實并不覺得“讓 JSR-14在工程上易于實現”是一個明確的目標。我們沒有說過“想要最小化增加泛型特性的工作量”。不過 Java 增加泛型的做法,比微軟平臺的做法要省事得太多太多了。微軟平臺不僅要對編譯器進行大量修改,還需要深度修改虛擬機和運行時類庫。我的意思是,從系統整體來看,需要進行更多更徹底的修改。
原則上講,這樣的改變不會破壞現有的類庫。不過如果你想要把現有的類庫遷移至泛型版本,則需要做進一步的修改,因為它著實改變了。我并不覺得 Sun 如果采取了 C# 的做法會對用戶帶來多大負擔,但 Sun 的工作量就會大一些了。
Sun 會有許多事情要做,事實上他們并沒有足夠的資源去做這些事情。
InfoQ:沒錯。
Neal Gafter:至少沒法在計劃時間給完成。要知道,這可能會需要花費好幾年的時間,還需要十幾個額外的開發人員才能實現微軟的做法。微軟對 .NET 平臺投入的研發資源總是比 Sun 的投入要大很多。而現在從功能角度來說,Sun 則需要比微軟投入更多研發資源了。
我認為微軟一般會提供更為徹底的設計、測試以及良好集成的系統──它的審查絕對更為仔細,系統的各個部分在一起工作時都比 Sun 更為清晰。舉個例子,在1.1版本中,Sun 為平臺同時添加了內部類和序列化功能。我沒有參與這部分工作,但就我的理解來看,負責這些功能的團隊在工作時都是相互獨立且正交的,但實際上不應該如此。
這些不是正交的功能,這導致這兩者直接之間的交互始終令人感到不舒服。我的意思是,這些功能的邊界上總有無可避免的弱點。如果投入更多時間,更多資源,更多測試和審查,雖然的確會需要更長的時間,更昂貴的開發成本,但我認為這無疑會得到更好的結果。就我的體會而言,C#在很多方面的設計比 Java 要扎實許多,泛型便是其中一例。
不知道這有沒有回答你的問題。
InfoQ:是的,謝謝。Java 社區長久以來有一個爭論,我想在 .NET 社區里也是如此,就是說“是否真有必要不斷添加特性”。會不會到某個時候語言變得足夠復雜了,因此必須停止添加特性?很多人拋出的例子是C語言,它基本沒有改進,但它始終運用廣泛可能也是因為這個原因。與此相反,C#卻在不斷提供快速的改進。你支持哪邊?你的觀點是什么呢?
Neal Gafter:我認為改進是必須的,但處理起來需要格外小心。Java 在這方面會比 C# 要困難,有幾點原因。
第一點:資源限制。
第二點:與 C# 等語言相比,Java 缺少長期的計劃。
C#的計劃十分清晰──我是指 Anders Hejlsberg 的計劃。他是整個語言和平臺的架構師,他的設計感非常好。盡管他對別人干涉很少,這就是他與同事一起工作時的協作風格,但在語言該何去何從的問題上他有著十分清晰的長期觀點。每次在改進時,他首先考慮的就是:這是讓語言朝著那個方向在前進,還是由于某人的偏好而跳向了另外一邊。
有很多事情會讓某些人感到滿意,但對更多人來說卻沒有多大收獲。如果某些東西在添加以后并與人無助,這實際上便是損害了他們的利益。盡管他們不會使用這些東西,甚至看也不會看一眼,也不會關心,但其實還是讓整個系統變得復雜了。
所以,我們在微軟內部考慮每個語言提案的做法,就像是從負1000分開始,在開始真正考慮將其添加到語言中之前,你還需要和這負1000分進行搏斗。之前我的感覺還只是負100分,但現在的確得看作是負1000分了。標準變的越來越嚴苛,對語言來說應該如此。
即便是C語言也在改進。它有標準委員會,新版本的標準出來之后的確會帶來改進,長期以來也有了大量變化。不過C語言的委員會對待改進的態度,的確比像 C++ 這樣的標準委員會要保守地多。
在考慮哪些該放入語言哪些該拒絕掉的時候,C++標準委員會的態度就寬松地多了。你知道,我并不反對保守主義,但我同時覺得如果一門語言在目前還是舉足輕重的話,就必須在一定程度上擁抱變化。
對 Java 來說,較難改變的原因之一,是很多在大道理上講是不錯的變化,但是他們的實現方式并沒有考慮語言的未來發展。這對我眼中的“長期計劃”來說其實是種倒退。
泛型便是最好的案例之一。在添加泛型特性時,一些人對人們移植類庫的復雜度有所顧慮,因此他們使用了擦拭法進行簡化。但問題是,擦拭法讓人難以添加后續的語言特性。例如,在使用擦拭法的泛型系統中,為語言添加函數類型就變得困難了許多。
InfoQ:如果我沒記錯的話,你在一篇博客中對此進行了辯護,說還是有辦法修補的。你可以實現一種不依賴擦拭法的泛型。是這樣嗎?我記得我看過這篇文章。
Neal Gafter:沒錯。事實上根據我的提議你可以同時擁有兩者,但最終你還是會得到非泛型的類型,擦拭后的泛型類型參數,以及具體化的泛型類型參數。你永遠無法避開擦拭法,有些類型參數始終會被擦除,還有一些則永遠不會。
但問題是,如果語言的未來演化是朝擦拭的方向進行的,那么這種做法不會帶來什么幫助。之前如果對語言產生了影響的,那么還是會繼續產生影響。對于那些想要使用泛型,且需要具體化類型參數以便實現某些功能的人來說,這的確會有所幫助。但擦拭法在許多方面依然會礙手礙腳。我知道也有人在探索如何可以在獲得具體化泛型的同時講破壞降低到最小。但我們做得到嗎?
這其實很難,不過也有些人有信心在這方面取得進展。如果他們成功了,那么理論上說,某次發布就會導致之前運行良好的一些泛型代碼出現問題,但之后你可以獲得具體化的泛型功能。如果真能做到了這點,我覺得就再好不過了,但對此我還是有所懷疑。
InfoQ:是啊,對此我也很矛盾,但的確很有趣。Stephen Colebourne 聲稱 Oracle 應該推出一個不向后兼容的 Java 版本,修復這些走錯路的問題,最好還能同時維護兩個版本。這樣……
Neal Gafter:好吧,這其實不就是微軟做的事情嘛,只不過改名叫做 C# 了而已。我的意思是,我會這樣問:為什么非要 Oracle 來搞?
為什么要叫做 Java?你知道,為什么非要跟 Java 扯上關系?如果不向后兼容,那么就不是 Java 語言了。如果你希望它運行在 Java 虛擬機上,并提供具體化的泛型功能,這也會遇到麻煩,因為具體化的泛型功能需要虛擬機的支持。
所以,既然是個新的虛擬機,也是個新的編程語言,那么為什么要 Oracle 來做這件事情?
這也是我給自己的問題。我絕對相信 Java 以外的語言也有發展空間,否則我也不會加入微軟了。即便是在 Java 虛擬機上也有其他一些很不錯的選擇,例如 Scala。
要知道,如果在 JVM 上我可以選擇語言來使用的話,Scala 在我的偏好里的排名會十分靠前。
InfoQ:在新興的語言里有哪些趨勢是值得關注的呢?你提到 Scala 是 OO 和函數式編程的混合體,我想 .NET 里的F#也是類似的東西。你覺得在超越函數式編程之后會是什么呢?
Neal Gafter:嗯,超越函數式編程?我想會有很多方面──我這么回答多少有些回避問題吧。
但我的確是這么想的,尤其是像 Java 和 C# 這種一開始是從命令式編程發展起來的語言,從函數式編程社區那里還有許多點子可以借鑒過來,這種挖掘探索還會持續幾年。
不過,我不覺得 Java 或 C# 能夠走的像你期望中那么遠,它們永遠不會成為函數式那樣的編程語言。我的意思是,它們的本質依舊會是命令式的,但你可以體驗到許多函數式編程方式所帶來的快感,尤其是使用不可變數據進行編程的時候。我想,如果你想真正獲得并發編程的優勢,但還是在使用傳統的鎖和信號量──就是那些 Java 很早就一直提供的東西──那么基本就無法進行良好的擴展。
你沒法用這種方式開發大規模的并發系統。好吧,可能你做得到,但我不行。在進行大量的數據操作以及 Fork-Join 并發編程時,不可變的數據是至關重要的,共享的可變數據往往會是問題的根源。
解決方案便是不要共享或不要修改。
而函數式風格意味著你不會進行修改。你不修改狀態,只會使用不可變的數據。在 Java 語言里支持這種風格還需要一些努力,類庫也一樣,尤其是基礎支持類庫更有許多事情要做。如果我們希望這種編程風格被人廣泛使用,則還有很長的路要走。
InfoQ:所以你的看法是,如果我們要提高編程效率,必須提供明顯的多核支持或是類似的東西?
Neal Gafter:是的,我認為這會以“潤物細無聲”的方式進行改變。
我認為,在實際操作的時候,我們會找到性能和穩定性問題所在,然后使用更易擴展的方式來逐一替代每個子系統。
InfoQ:一些現有的做法,例如 Actor 模型或是 Erlang 那樣的消息傳遞方式,你覺得它們對于 Java 或 C# 來說是否合適呢?
Neal Gafter:有這個可能,不過這需要語言方面的一些改變,沒人計劃朝這個方向去走。Actor 風格的關鍵之一是模式匹配,Scala 用 case 類來進行模式匹配。
目前還沒人打算在 Java 里實現這個。我想這個特性還是值得考慮的,但我還是認為這需要一個高屋建瓴的長期計劃,這樣我可以清晰地看到這將成為 Java 的一部分。
但你不能缺少一個長期計劃就開始做事,不能因為只是有人在那里說“啊哈,在版本X里我們要加入 Actor 模型”,一定需要一個長期計劃。目前在 Java 里創建一個表示不可變數據的類型還很尷尬,應該提供一些更簡單的辦法。此外也應該能夠輕松地進行模式匹配。我覺得目前的序列化功能在處理不可變數據時還不夠理想,但可能也不是問題?即便 Actor 模型與目前還沒影的不可變數據類型會是絕配,但目前的 Java 還沒有準備好,它們搭配起來會有問題。
我估計未來一定會有其一席之地,因為它的價值無庸置疑,但我還沒有從 Oracle 那里聽說這方面的消息,所以肯定不會是 Java 7 或8。如果它真會出現,那也是在這些時間計劃以外了吧。
不過我認為,如果你希望用那種形式編程──這的確會帶來許多優勢──那么你(至少現在)應該使用另一種更自然的語言。對 Java 和 C# 這種語言來說,想要自然的表達 Actor 模型還需要太多改變。所以使用 Scala 吧,或者 .NET 平臺上的F#。
InfoQ:我猜 .NET 平臺和 Java 平臺的一大趨勢便是支持 C# 和 Java 外的其他語言,只不過微軟從一開始就強調了而已。
Neal Gafter:當然,你看到 Java 里面有 java.lang.Object,對吧?
InfoQ:是啊。
Neal Gafter:它應該是面向所有語言的的對象,只不過當初在一開始的時候大家都還缺少這方面概念而已。
InfoQ:喔。John Rose 在領導一個 Java 項目,這也是 Da Vinci 項目的一部分,它支持像尾遞歸這樣的東西。如果你有機會做一些面向未來的項目,你會做什么呢?它會是什么樣的?
Neal Gafter:這個大方向其實是想要嘗試去做好幾種不同的東西。例如支持動態語言,目標之一便是希望讓這個虛擬機更容易實現動態語言,這也是 Da Vinci 項目最主要關注的東西。你想,我們可以加入哪些東西來支持不同的編程語言?還有便是如何讓平臺上不同語言之間的互操作變得更為簡單。這也是微軟的目標,它提供了 DLR 以及 C# 語言里的 dynamic 關鍵字,希望可以增加語言之間的互操作性。
微軟做的那些事情,是希望讓那些語言在提高執行效率的同時,還支持高效的動態分派,還提供了一種元對象(meta-object)協議。這樣,某種動態語言里的對象,也能被另一種編程語言通過一種合適的語義進行訪問,這樣語言之間便可以自由互通了。
我的想法是,無論是在虛擬機層面還是虛擬機之上增加這樣一種元對象協議,對于動態語言之間的互操作都是很有好處的。
InfoQ:后者就是 .NET 的做法,是嗎?它是 CLR 上的一個類庫。
Neal Gafter:是的。嗯,事實上 CLR 還是提供了一些支持的,不過基本上可是認為是 CLR 上的類庫。
還有一個方面便是 Java 編程語言上的支持。在 C# 里增加了一個“dynamic”類型,它的意義是:“在運行時探究其語義。我們在編譯器指定要做哪些事情,但在運行時確定其語義”,這便讓 C# 能夠使用動態語言創建的對象了。
所以我認為提供一個動態的元對象協議是個十分有用的方向。
在支持動態語言方面,我也有很多東西是樂于在虛擬機里見到的,尾遞歸便是其中一項。這不是個大功能,但對于某些編程語言來說至關重要。
還有便是分段式棧(segmented stack)。目前在 Java 和 C# 里都有這樣一個問題,這些平臺避免了很多C或 C++ 這類原生語言容易出現的穩定性問題,例如越界錯誤,因為每次訪問數組下標時都會進行范圍檢查。你也不能訪問已經釋放的內存,或是還未分配的內存,因為你不能直接操作內存空間,這是垃圾收集器做的事情。但是這些平臺目前還沒有合適的處理棧溢出的方法。棧溢出并不代表一定是出錯了,可能只是出現了預期以外的無限遞歸而已。
棧溢出是基本是因為遞歸造成的。我是做編譯器的,如果你想要讓 Java 編譯器崩潰,只要簡單寫一句“int i = 1 + 1 + 1 + 1”重復幾千次就行了。語法分析器或解析器會去分析這句代碼,然后棧就爆了,其結果便是進程崩潰了。
要知道出現這個問題后很難補救?。可能你會說:“要不就重頭開始吧,我們來分配更大的棧空間”。但問題是,你很難判斷該分配給這個線程多大的棧空間。
像 Google 的 Go 系統里提供的分段式棧在這方面的優勢在于,你無需提前決定棧空間的大小。它會根據程序的需求動態調整,這讓很多程序寫起來就容易多了。
Java 的線程十分昂貴,所以會有線程池。如果不貴就沒必要將它們池化了。它們昂貴的原因是占空間需要提前分配好,這些棧空間都太大了,會占用太多資源。你肯定不想因為分配很大的棧空間導致所有資源用盡,而有了分段式棧以后,線程之類的東西就輕多了。
線程的代價降低以后,很多東西就應運而生了。例如異步,很多人看到異步編程就想到狀態機或 co-routine。如果線程足夠輕便,那么這些就都不需要了。你只要創建一個線程,然后讓它等待著,因為在等待時并不會占用任何資源。
這樣在虛擬地址空間里甚至不會有棧分配這回事情了,所以我覺得在 Java 里考慮一下這方面問題是很有價值的。
這會給平臺帶來細微,但我相信也是十分重要的變化,也很有好處。這會影響你思考這個平臺上哪些東西是需要的或是不需要的。所以要我說的話,我會把票投給分段式棧,或者是例如虛擬機級別的 co-routine 的東西。
InfoQ:非常感謝您接受采訪。
受訪者簡介
Neal Gafter 是 Java SE 4 和5語言增強的主要設計者和實現者,他的 Java 閉包實現贏得了 OpenJDK 創新者挑戰賽的大獎。他也在繼續參與 SE 7 和8的語言發展。之前 Neal 在為 Google 的在線日歷工作,也曾經是 C++ 標準委員會的一員,并曾在 Sun 微系統公司,MicroTec 研究院和德州儀器領導開發C和 C++ 編譯器。如今 Neal 在微軟開發 .NET 平臺編程語言。Neal 是《Java Puzzlers:Traps, Pitfalls and Corner Cases》(Addison Wesley,2005)一書的合作者。他擁有羅徹斯特大學大學計算機科學的博士學位。
查看原文:http://w?ww.infoq.com/articles/neal-gafter-on-java
來自: InfoQ