爲何咱們須要如此多的JVM語言?
在2013年你能夠有50中JVM語言的選擇來用於你的下一個項目。儘管你能夠說出一大打的名字,你會準備爲你的下一個項目選擇一種新的JVM語言麼?php
現在藉助來自像Xtext和ANTLR這樣的工具的支持,建立一種新的語言比之前容易多了。個體編碼者和羣體受突破和改進現存JVM語言,以及傳統Java的限制和缺陷的驅使,讓許多新的JVM語言應運而生。html
新的JVM語言開發者感受他們的工做室針對現存語言的產物——現存的語言提供了太過受限制的功能,要不就是功能太過繁雜,致使語言的臃腫和複雜。軟件開發在一個廣闊的範圍被應用,因而一種語言的有效性就決定於它跟特定任務領域的相關性,或者它如何在更普遍的範圍中通用。全部這一切致使了資源庫和框架的開發。前端
大部分人大談特談JAVA語言,這對於我來講也許聽起來很奇怪,可是我沒法不去在乎。JVM纔是Java生態系統的核心啊。
James Gosling,
Java編程語言的創造者 (2011, TheServerSide)
如此多的語言存世,語言的墳場是否會飽滿呢?這裏有一個潛在的風險,市面上可供使用的選擇太多,將會致使許多語言因爲足夠的關注和社區貢獻而沒法生存發展下去。java
然而要在這個行業生存下去,必須基於創新和創造——這些每每來自於一個從零開始,而且放棄現有的抱怨和成見,在一塊白板上面起家的項目。git
這裏有一條咱們將天然而然遵循的線索:現有的語言建設和框架幫助建造起來的社區支持了Java生存,而且也使得下一代Java和新的創意、結構和範式,這些東西的產生成爲可能,最終將使它們的方式體如今現存的語言當中。程序員
Rebel Labs的報道了概覽了Java 8,Scala,Kotlin,Ceylon,Xtend,Groovy,Clojure和Fantom。可是如此多的JVM語言可供選擇,咱們如何會只看中這8種選擇?github
Rebel Labs 的團隊就如何出這樣一份報告,還有選擇哪一種語言,進行了六個月的討論。最基本的,咱們想要呈現給每一個人一些東西:Java是一種極其著名,應用普遍的語言,可是Java 8擁有許多咱們想要一探究竟的新東西。Groovy,Scala和Clojure已經找到了它們在市場中的核心競爭力,而且變得愈來愈流行起來,而像Ceylon,Kotlin,Xtend和Fantom在咱們的涉獵中相對還比較新穎,須要經受一些考察來得到信任。web
咱們的目標是創建對每一種語言的認識,它們是如何進化的,將來將走向何方。所以在這份報告中,你將會看到咱們闡述對於這些語言的第一印象,包括那些給咱們帶來震撼的特性,以及不那麼能打動人的特性。shell
你將會看到一個HTTP服務器基礎實現的源代碼示例,它連接到了GitHub,所以你能夠同咱們一道來探討它。express
一小段歷史
最開始只存在Java,它是用於在JVM上編程的惟一選擇。可是這個行業很早就知足了對在JVM上面編程的更多和潛在的選擇需求。在腳本領域首先出現了Jython,JVM的一種Python實現,還有Rhino和JavaScript的JVM引擎,它們出如今1997年,跟着就是2000年的BeanShell和2011年的JRuby。
因爲對於動態定製應用程序的需喲,在那時腳本設施的需求很旺盛。現在,像Oracle WebLogic和IBM WebSphere這些應用服務器都使用Jython腳原本執行自動化操做,而Rhino也被綁定在Java 6上面,使得JavaScript成了JVM上的一等公民。
然而,腳本設施不是惟一一個讓基於JVM的可選編程語言滋生的因素。歸因於Java的向後兼容性原則,爲了提供一些Java或者它的標準庫沒有提供的新穎特性,可選語言開始出現了。Scala和Groovy就是最早提供了一些東西超越了Java的成功項目.
咱們能夠觀察到一種有趣的現象:大部分新晉的編程語言都利用了靜態類型。使用Scala,Ceylon,Xtend,Kotlin和Java自己的開發者都依賴於在編譯時驗證目標類型。Fantom在動態和靜態類型之間找到黃金的平衡中點,而Groovy,儘管一開始是一種動態語言,可是現在也在其2012年的2.0發行版中也開始加入編譯時的靜態類型檢查了。Clojure——有點Lisp的味道——是堅持使用動態類型,但惟一還收到合理擁泵的JVM編程語言,然而一些在企業的大型團隊中工做的開發者擇認爲這會是Clojure的一個缺陷。
運行在JVM上的新的編程語言,已經有從定製化應用程序的動態腳本語言,向着通常意義的靜態的應用程序開發語言改變的趨勢。
Java仍然是最常使用在JVM上的編程語言,而隨着Java 8發行版的到來,Java將嘗試在語法美學和多核編程方面,跟上時代的潮流。
在 Github Repo 上代碼樣例
在幾個JVM語言的引擎下這會變的很geek。 在這篇文章中,咱們重新的角度看Java(換句話說, 在Java 8中), Scala, Groovy, Fantom, Clojure, Ceylon, Kotlin 和Xtend–mostly, 而且給出最吸引咱們和咱們最深入的印象。
每個語言都有本身的 HTTPServer 樣例 ,它們都在 github 上。你能夠檢查咱們的代碼,全部在這篇文章的JVM 語言 都在這:
https://github.com/zeroturnaround/jvm-languages-report
JAVA 8
「我真正關心的是Java虛擬機的概念,由於是它把全部的東西都聯繫在了一塊兒;是它造就了Java語言;是它使得事物能在全部的異構平臺上獲得運行;也仍是它使得全部類型的語言可以共存。」
James Gosling,
Java編程語言的創造者 (2011, ServerSide)
Java 8 入門
JavaSE 8.0是值得期待的。
讓咱們來看一看Java平臺的整體演變策略:
- 不去打破二進制代碼的兼容性
- 避免引入源代碼級的不兼容
- 管控行爲方式級別的兼容性變動
簡單來講,目標就是保持現有的二進制文檔可以連接並運行,而且保持現有的源代碼編譯可以經過.向後兼容的政策已經影響到了Java這種語言的特性集,同時也影響到了這些特性如何被實現.例如,使用目前的Java特性不可能促進API的進化,由於變動接口可能會打破現有依賴於JDK接口的資源庫,其源代碼的兼容性.這就產生了一個同時影響到語言和JVM的改變.
隨着Jigsaw——模塊化的主題——正從Java 8中取締,Lambda項目成爲了即將到來的發行版中最重要的主題.儘管其名號有一點點誤導性.可是lambada表達式確實是其一個重要的部分,它自己並非什麼重要的特性,但倒是Java在多核心領域要作出努力的一個工具.
這個多核心的時代有對於並行庫的需求,而且也對Java中的集合(Collection)API形成了壓力.這下就須要lambda表達式使得API更加友好和易於使用.防護式方法是API革命的工具,而且也是現存的集合庫將如何向支持多核邁出步伐的基礎.
那麼你想要使用lambda,嗯?
若是你熟悉其它包含lambda表達式的語言,像Groovy或者Ruby,你將會驚喜與它在Java中是如此簡單.在Java中,lambda表達式的做用表如今"SAM類型"——一個擁有抽象方法的接口(是的,接口如今能夠包含非抽象的方法了——叫作防護方法)。
那麼舉個例子來講,著名的的Runnable接口能夠完美地適合於做爲一個SAM類型提供出來:
Runnable r = ()-> System.out.println("hello lambda!");
這也一樣適用於Comparable接口:
Comparator<Integer> cmp = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0);
一樣也能夠像下面這樣寫:
Comparator<Integer> cmp = (x, y) -> {
return (x < y) ? -1 : ((x > y) ? 1 : 0);
};
這樣就看起來彷佛像是一行lambda表達式擁有隱式地語句返回了.
若是你想寫一個可以接受lambda表達式做爲參數的方法該怎麼作呢?那麼你應該將這個參數聲明爲一個功能的接口,而後你就可以把lambda傳進去了。
2 |
void run(String param); |
4 |
public void execute(Action action){ |
一旦咱們擁有了一個將功能接口做爲參數的方法,咱們就能夠像下面這樣來調用它了:
1 |
execute((String s) -> System.out.println(s)); |
一樣的表達式能夠用一個方法引用來替換,由於它只是一個使用了相同參數方法調用。
1 |
execute(System.out::println); |
然而,若是在參數在進行着任何變化,咱們就不能使用方法引用,而只能使用完整的lambda表達式了:
1 |
execute((String s) -> System.out.println( "*" + s + "*" )); |
這裏的語法是至關漂亮的,儘管Java自己沒有功能(functional)類型,可是如今咱們已經擁有了一個優雅的Java語言的lambda解決方案。
JDK 8中的函數型(Functional)接口
如咱們所瞭解到的,一個lambda在運行時的表現是一個函數型接口(或者說是一個「SAM類型」),一種只擁有僅僅一個抽象方法的接口。而且儘管JDK已經包含了大量的接口,像Runnable和Comparable——符合這一標準,對於API的革命來講仍是明顯不夠用的。而在整個代碼中大量使用Runnable,也可能不怎麼符合邏輯。
JDK 8中有一個新的包——java.util.function——包含了許多應用於新型API中的函數型接口。咱們不會在這裏將它們所有列出來——你本身有興趣的話就去學習學習這個包吧:)
因爲一些接口的此消彼長,看起來這個資源庫正在積極的進化中。例如,它曾經提供了 java.util.function.Block類,可是在咱們寫下這份報告時,這個類型卻沒有出如今最新的構建版中了:
2 |
openjdk version "1.8.0-ea" |
3 |
OpenJDK Runtime Environment (build 1.8.0-ea-b75) |
4 |
OpenJDK 64-Bit Server VM (build 25.0-b15, mixed mode) |
如咱們所發現的,它已經被Consumer接口替代了,而且被應用於集合資源庫中的全部新方法中。例如,Collection接口中像下面這樣定義了 forEach 方法:
1 |
public default void forEach(Consumer consumer) |
Consumer 接口的有趣之處在於,它實際上定義了一個抽象方法——accept(T t),還有一個防護型的方法—— Consumer chain(Consumer consumer).。這意味着使用這個接口進行鏈式調用是可能的。咱們尚未在JDK的資源庫中找到 chain(...) 方法,所以還不怎麼肯定它將怎樣被應用。
並且,請注意全部的接口都標記上了@FunctionalInterface(http://download.java.net/jdk8/docs/api/java/lang/FunctionalInterface.html)運行時註解。可是除了它在運行時經過註解用javac去確認是否真的是一個功能型接口之外,它裏面就不能有更多的抽象方法了。
所以,若是你編譯下面這段代碼:
3 |
void run(String param); |
4 |
void stop(String param); |
編譯器將會告訴你:
1 |
java: Unexpected @FunctionalInterface annotation |
2 |
Action is not a functional interface |
3 |
multiple non-overriding abstract methods found in interface Action |
而下面這段代碼將會正常的編譯:
3 |
void run(String param); |
4 |
default void stop(String param){} |
防護方法
出如今了 Java 8 中的一個新概念是接口中的默認方法。它意味着,接口不只能夠聲明方法的簽名,也還能夠保持默認的實現。對於這個功能的需求源於對於JDK API中的接口進化須要。
防護方法最顯著的應用是在Java的Collection API。若是你使用過Groovy,你可能寫過像下面這樣的代碼:
1 |
[ 1 , 2 , 3 , 4 , 5 , 6 ]. each { println it } |
而現在,咱們使用像下面這樣的for-each循環來進行迭代操做:
1 |
for (Object item: list) { |
2 |
System.out.println(item); |
可以使用這個循環多是由於 java.util.Collection 接口擴展了 java.util.Iterable 接口,這個java.util.Iterable接口只定義了一個Iterator iterator()方法。要是想Java利用Groovy類型的迭代,咱們就須要Collection和Iterable中都有一個新的方法。然而,若是添加了這個方法,就將打破現有集合資源庫的源代碼級別的向後兼容性。所以,在Java 8中,java.util.Iterable 添加了forEach方法,而且爲它提供了默認的實現。
1 |
public interface Iterable<T> { |
3 |
public default void forEach(Consumer consumer) { |
添加新的默認方法並無打破源碼級別的兼容性,由於接口的實現類並不須要提供它們本身對於這個方法的實現,所以從Java 7切換到Java 8之後,現有的代碼還能繼續經過編譯。如此,在Java8咱們可以像下面這樣編寫循環代碼:
1 |
list.forEach(System.out::println); |
forEach方法利用了一個功能性接口做爲參數,於是咱們可以將一個lambda表達式做爲一個參數傳遞進去,也或者能夠像上面的代碼示例同樣是一個方法引用.
這種方法對於多核場景的支持是很重要的,由於使用這種方式你自信的忽略掉循環的細節原理而專一於知足真正的需求——你所依賴的資源庫幫助你打理了循環的細節。新的lambda表達式自己對Java開發者並無多少意義可言,由於沒有集合資源庫合適的API,使用lambda表達式不太可能可以充分的知足開發者們.
咱們詢問了不一樣JVM語言的建立者和項目領導人對於Java8中新特性的見解
SVEN EFFTINGE——XTEND
是的,它們是徹底必要的,而且是朝着正確方向的一個良好開端.Xtend將使用Java8做爲可選的編譯目標,從何生成的代碼獲得改善.
Java8的lambda表達式同Xtend中的lambda表達式在語義上很是相似.新的流API可以毫無麻煩的同Xtend良好工做.事實上,它在Xtend上比在Java8上面工做得更好.關於這個我已經寫了一篇文章[http://blog.efftinge.de/2012/12/java-8-vs-xtend. html]:-).相較於Java8的流(stream)API,我仍然更傾向於選擇Guava API,由於它們更加方便並且可讀性更高。
防護方法也是一個不錯的東西,儘管我不喜歡使用'default'關鍵字這種語法.他們掙扎過接口和類方法不一樣的默承認見性.我想他們是在嘗試得到一種明顯的語法上的區別,以便人們再也不混淆類和接口.Xtend中雷和接口的默承認見性是相同的,在這兒那將不是問題.
BRIAN FRANK – FANTOM
可能挺激動的:-) 多年之後,現在實際上每個現代的語言都已經有了基本的函數式編程機制.然而仍舊有大量的使用着Java的程序員不知道這些概念的,所以我想向更多的開發者灌輸更多的函數式編程風格將會是有益的.咱們不認爲函數式編程時靈丹妙藥,可是確實是工具箱中很給力的一種工具.
GAVIN KING – CEYLON
Java8在某種程度上從新點燃了開發者對Java平臺的興趣,使他們迴歸Java,那對於Ceylon和其餘基於JVM的語言來講是很是美好的事情.
Java8使得一大堆常規的編程任務執行起來更加的方便.當時,從另一方面來看,通過多年的嘗試,Java SE的團隊仍然沒有推出內置的模塊化功能,這令我極其失望.他們在Java8上所做的良好工做絕對值得讚賞,然而失足於這樣一個關鍵之處,給予一樣的批評,我想纔是公平的.Lambda是使用和方便的語法糖.可是模塊化纔是有關Java一切的關鍵之處,而且是它在某些領域失敗的關鍵緣由.
JOCHEN THEODOROU – GROOVY
說我不興奮,確實.裏面有不少Groovy已經實踐了不少年的東西.防護方法在我看來就像個半拉子步調,還有就是我不怎麼喜歡這樣使接口 混淆.Lambda表達式對我來講更加有意思,可是我發現Groovy的閉包(Closure)是更增強大的概念。Lambda表達式確實可以使Java成爲一門更好的語言,可是它們也會使得Java的一些明智的概念複雜化.我想當肯定,若是沒有Scala和Groovy的話,這些特性也許永遠不會出現.它們(指的是這些特性)是Java對來自衆多有競爭力的其它可選JVM語言的壓力而作出的反應。並且,它們也不得不在保持領頭羊地位和吸引高級用戶之間保持複雜的平衡.於是它們被困在了中間的某個地方,隨之產生了lambda表達式和防護方法.
GUILLAUME LAFORGE – GROOVY
這裏我並不像Jochen那樣消極,儘管在其餘語言如何影響Java朝那條路發展這一點上,他的觀點同實際狀況相差並不遠.
雖然Java8的lambda表達式、推倒重來的"流(Stream)"式集合或者防護方法實際上並不如咱們所想象的那樣,可是我認爲全部那些東西結合起來應該可以給開發者們進化他們的API帶來一些新的東西,而且它應該有但願更好的設計和精簡新老框架的使用.因此我想,整體觀之,這對於Java來講是好事.
ANDREY BRASLAV – KOTLIN
Java變得更好意味着千萬開發者變得更加快樂.Kotlin可以使他們中的一些人更加的快樂,但這是另一碼子事了:)
只須要"使用閉包(clusure)的Java"的人們將會在Java8中得到它(指閉包),而且會很高興。可是還有另一些人,他們不只僅只須要匿名函數(那也確實是很是重要的,可是整個世界可不止於此哦).
Scala
「意在使其端正,而不塞入太多的語言特性到其裏面,我在Scala上專一於使它變得更加的簡單.那是人們經常有的一種誤解,他們認爲Scala是一種帶有許許多多特性的宏大語言.儘管這一般不是真的.它其實是一個至關小的語言——當Java8面世之時它將比Java更加的小巧。」
Martin Odersky,
Scala 創始人
Scala入門
同本報告中涵蓋的大部分語言相比,Scala是至關行之有效的.2003年已經有了它的第一個發行版,可是從2006年纔開始出如今許多雷達(radar)上,當時其2.0版本在EPFL上發行了.從那時起它就日益普及,而且有可能接近於與一線語言爲伍了,你能夠參考語言排行榜( language ranking )來確信這一點.
從2006年開始,它的許多表現令其變得有趣起來——就是使用類型推斷混合了面向對象編程(OOP)和函數式編程(FP:Funcitional Programming)的一種靜態類型;雖然不是原生的,可是被編譯成了高效的代碼。它擁有模式匹配、帶底層類型的先進類型系統、代數數據類型、結構類型甚至依賴類型。它也使得表達像單子(monad)這樣的分類理論抽象變爲可能,可是你能夠在本身內心決定是否去在乎這個東西。及時你在標準庫中使用了一些單子,可是甚至你也可以在不知道單子是什麼的時候那樣作。
世上可沒有什麼典型的Scala開發者這一說——一些使用Scala的人是Java開發者,他們想要擁有更具表達能力的語言,還有一些是函數式編程者,他們發現了這是一種在JVM上使用的上佳語言。這意味着Scala程序可以被編寫成許多徹底不一樣的風格——純函數式風格的,勢必不純函數式的,或者二者的混合風格。你甚至能夠交叉使用這些風格,使用盡量抽象的方式,利用先進的類型系統(見 Scalaz&Shapless 資源庫,或者只比在你會在Java代碼中使用的更多一點點的抽象。
從2006開始,Scala已經經歷了一些重大的變化。其中最大的一個變化是Scala2.8中通過大大調整的集合API,它多是任何語言中最強大的了,可是相比於大多數的集合庫,其實現細節也具備更多的複雜性。版本2.9添加了並行集合(parallel collection),而版本2.10帶來了一大堆特性,其中一些是實驗性質的:衛生宏( hygienic macros)、新的反射庫、字符串差值(String interpolation)、大大加強的模式匹配代碼生成器,還有更多的其它特性。
同Java的主要區別
在使用Scala實現的HTTP服務器(HTTPServer)樣本中,咱們可以立刻注意到它摒棄了static關鍵字,而不像Java中(這個HTTP服務器)將會是一個伴隨/單例(Companion/Singleton)對象中的靜態成員。一個伴隨(companion)對象是一個與類同名的對象。因此實際上,咱們將咱們的HttpServer切分紅了一個對象和一個類——對象擁有靜態的部分,而類擁有動態的部分。這些東西在Scala中是兩個不一樣的命名空間,可是爲了方便咱們可以引入動態部分中的靜態命名空間:
Scala容許在任何地方使用引入(import)語句,你能夠從對當前範圍可見的擁有成員的任何地方,將成員引入到當前範圍之中;所以Scala代碼中的這個結構(指import)時間上比Java更加的經常使用,而Java只容許你將引入語句放在文件的開頭,而且只能引入類或者類的靜態成員。
與Java比較,還有一個鮮明出衆的地方,那就是方法使用def name(argument:Type)的形式定義,而且變量也是如此定義:
1 |
val name: Type = initializer |
或者是:
1 |
var name: Type = initializer |
你應該不會去選擇使用可變的變量,因此默認使用val吧——咱們的樣本代碼中並無任何使用var的定義,由於實際上若是你過去大可能是寫的Java代碼,那麼它們比你所想象的有更少的使用機會。你經常可讓類型聲明遠離定義,而且讓它本身去推斷,可是方法的參數必須老是有明確的類型。
從main()方法能夠看出,從Scala調用Java代碼經常是很容易的,它經過Executors類建立了一些像ServerSocket這樣的一些Java對象。
Case類和模式匹配
Scala中比較有趣的一個特性是case類,它像是一種普通的類,可是帶有編譯器生成的equals、hashCode、toString、支持模式匹配的方法等等。這讓咱們能夠用不多的幾行代碼創造保存數據的小型類型。例如,咱們選擇使用一個case類保存HTTP狀態行的信息:
1 |
case class Status(code: Int, text: String) |
在run()方法中,咱們可以看到一個模式匹配的表達式。它同Java中的switch相似,可是更增強大。然而,這裏咱們不去深刻了解它真正的強大之處,咱們只使用一個 「|(或運算)」模式,還有你可以經過使用 name @的前綴 將一個匹配結果綁定到一個名字(name).
咱們使用的模式是要去匹配一個從HTTP鏈接輸入流讀取的HTTP方法名。第一次的模式中咱們匹配「GET」或者「HEAD」,第二次則是其它的任何東西:
1 |
case method @ ( "GET" | "HEAD" ) => |
5 |
Status( 501 , "Not Implemented" ), |
6 |
title = "501 Not Implemented" , |
7 |
body = <H2> 501 Not Implemented: { method } method</H2> |
在第二種狀況下,當調用respondWithHtml時,咱們利用了(默認)已經命名的參數——它們容許咱們在調用方法的站點命名參數,以免不得不去記憶相同類型參數的順序,或者僅僅是爲了使代碼更加純淨。這裏咱們選擇了不去命名狀態,由於從Status(...)構造器的調用看來,它的意義已經很明顯了。
有趣的String
另一個有趣的特性——它在Scala2.10中被加入——它是String差值。你在編寫常規的String常量時帶上s前綴,它就容許你在String中嵌入Scala代碼,使用${}或者$來使得簡單名字區分識別出來。同多行的String結合起來,咱們能容易的構造出將被髮送的HTTP頭部,不帶任何String的串聯操做:
2 |
|HTTP/ 1.1 ${status.code} ${status.text} |
3 |
|Server: Scala HTTP Server 1.0 |
5 |
|Content-type: ${contentType} |
6 |
|Content-length: ${content.length} |
8 |
"" ".trim.stripMargin + LineSep + LineSep |
(注意:咱們能夠選擇丟棄零參數方法的括號。)
Scala也容許咱們實現咱們本身的String插值,可是這裏用默認實現的已經足夠了。
trim方法是一般的Java的String.trim()方法,它用於從頭至尾去掉字符串中的空格字符(也包括換行符)。stripMargin方法則將去掉字符串每一行從開始直到 | (尾部連結)符號的全部東西,而且容許咱們在多行字符串上面正常使用縮進。這個方法經過從String到WrappedString的隱式轉換被添加到String類型中,而且如法炮製,咱們能夠添加咱們本身的邊緣剝離(margin stripping)邏輯,例如作到讓你能夠在沒有額外的 | 字符的前提下,對每一行執行trim操做。
內置XMl,愛它仍是恨它
在respondWithHtml方法中,咱們看到另一個有趣但不那麼可愛的Scala特性:內置XML表達式。該方法用一系列的XML節點(scala.xml.NodeSeq),以XHTML子元素形式,做爲輸入參數,而後將它們包裹於另外一個XML表達式之中,在這些實際的title和body周圍增長了HTML/HEAD/BODY元素,再將它轉換爲字節。咱們調用這個方法時,咱們能夠爲body提供XML表達式形式的元素。
def respondWithHtml(status: Status, title: String, body: xml.
NodeSeq) =
...
<HTML>
<HEAD><TITLE>{ title }</TITLE></HEAD>
<BODY>
{ body }
</BODY>
</HTML>
...
避免空指針錯誤
在toFile和sendFile中,咱們使用Scala處理可選值的首選方法,選擇類型(請注意:Scala也有空值)。toFile會返回一些(文件)或者若是沒找到服務的文件則不反悔文件,而後sendFile會作一個涵蓋兩種狀況的模式匹配。若是咱們遺漏了任何一種狀況,編譯器都將警告咱們該狀況。
1 |
def toFile(file : File, isRetry : Boolean = false ) : Option[File] = |
2 |
if (file.isDirectory && !isRetry) |
3 |
toFile( new File(file, DefaultFile), true ) |
全部東西都是表達式
咱們也能利用這一事實——那就是在Scala中幾乎全部的構造都是表達式——因而像if-else-if-else這種控制結構實際上就產生一個值。所以咱們可以省略掉括弧,直接讓方法使用一個表達式。
在sendFile方法中咱們能夠見到更多的這種東西,在裏面咱們使用了本地的{...}塊,它產生了一個值——這個塊的最後一行表達式是它的返回值,如此咱們隱藏了塊中的臨時變量,而且將結果分配到塊的最小的一個臨時變量中。
2 |
val fileExt = file.getName.split( '.' ).lastOption getOrElse "" |
3 |
getContentType(fileExt) |
儘管這裏沒有展現出Scala完整的深度,可是咱們看出它具備的表現能力,並且這個例子如期提供了關於這個語言是什麼的一些觀點。關鍵是擁抱不變性,而且嘗試將代碼組合建模成表達式。所以相較於咱們提供的樣本,toFile方法彷佛最好從sendFile中提取出來。
Groovy
「Groovy有超過Java將可以提供的甜點,例如它具備輕易地在宿主程序中嵌入並編譯,以提供定製業務規則的能力,還有它如何爲領域特定語言(Domain-Specific Language)提供優雅,簡潔而且可讀性好的語法的能力.」
Guillaume Laforge,
Groovy的項目帶頭人
Groovy入門
Groovy並不像咱們在這個報告中涵蓋的一些語言那樣具備冒險性質,但絕對是你應該感興趣的一種JVM語言。它已經成爲了一種受到開發者信任的成熟選擇,這時Java開發商的傷害,以及動態類型這些都不是問題。
不管如何,我都不會是那種爭論什麼時候給玩轉一種編程語言一次機會的人。
Java變得過度充實
Java開發者能夠在Groovy中深刻編程而且變得多產。匹配Java的語言有但願或者這看起來將會是將來的趨勢,2.0發行版中已經加入了Java7項目的Coin加強.另外Groovy還使得平常使用Java遇到的坎坷變得平滑。安全的導航(?.)以及 Elvis(?:)都算是很棒的例子。
3 |
def streetName = user?.address?.street |
5 |
def displayName = user.name ? user.name : "Anonymous" |
7 |
def displayName = user.name ?: "Anonymous" |
「Groovy是一種多樣性的JVM語言.使用一種同Java相近的語法,這種語言是易學的,而且容許你編寫從腳本到完整的應用程序代碼,包括強大的DSL(領域特定語言)。Groovy極可能是JVM上惟一使得運行時的元編程、編譯時的元編程、動態類型以及靜態類型容易處理的語言。」
CÉDRIC CHAMPEAU,
Groovy中的高級軟件工程師
閉包(Closure)
咱們預期Groovy將止步於句法功能,然而咱們卻在文檔中又發現「閉包」。爲何稱這超越了咱們的預期呢,由於Groovy函數值中的一等公民、更高級別的函數以及lambda表達式,這些都獲得了支持。
2 |
[ 1 , 2 , 3 , 4 ]. collect (square) |
標準庫對於閉包恰到好處的應用使得使用它們成爲一種享受,而且也證實了它們的實力。下面是使用閉包的語法糖做爲方法後面參數的好例子:
1 |
def list = [ 'a' , 'b' , 'c' , 'd' ] |
3 |
list. collect ( newList ) { |
集合
幾乎全部的應用程序都依賴於集合。不幸的是集合大量的戳到了Java的痛處。而若是你懷疑個人這種說法,請嘗試作一些有趣的JSON操做。Groovy爲集合的定義將原有的語法打包,而且爲了強大的可操做能力,着重使用了閉包。
1 |
def names = [ "Ted" , "Fred" , "Jed" , "Ned" ] |
3 |
def shortNames = names.findAll { it.size() <= 3 } |
4 |
println shortNames.size() |
5 |
shortNames.each { println it } |
靜態類型
人們經常振奮於動態語言,由於你用不多的代碼就能得到更多的功能。這經常不多被理解,而剩下的被帶回去維護。所以咱們可以看到愈來愈多的動態語言得到靜態類型,並且反之亦然。
Groovy2.0加入了靜態類型
靜態類型緣何且如何提高了Groovy?
「靜態檢查使得從Java到Groovy的轉型的之路更加的平滑。許多人加入(後續還有更多人加入)Groovy,由於它輕量級的語法,以及全部被移除的樣板,可是,舉個例子,不想(或者不須要)使用動態特性。他們每每很難理解Groovy編譯時不像他們之前那樣拋出錯誤,由於他們實在是不能理解Groovy是一門動態語言。對於他們來講,咱們如今有了@TypeChecked。第二個緣由是性能表現,因爲Groovy仍然支持更老的JDK(如今是1.5),而動態調用支持對它們不可用,所以爲了代碼的關鍵性能部分,你能夠有靜態的編譯了的代碼。還要注意的是,靜態編譯對於那些想要免受猴急修補(monkey patching)的框架開發者來講是有趣的(基於可以在運行時改變一個方法的行爲這一事實)。」
Cédric Champeau
Groovy的高級程序工程師
Groovy也不例外,靜態檢查能夠經過相關代碼中的@Typechecked註解實現.
01 |
import groovy.transform.TypeChecked |
02 |
void someMethod() {} <&br> |
你最喜歡的,用Groovy寫成的應用程序/框架/庫是什麼?還有爲何你對它飽含激情?你能說出超過一種麼?
「那很簡單,我最感到熱情的就是Griffon,一個基於JVM的桌面應用程序開發平臺。Groovy被用於做爲框架中的原生語言,而其餘JVM語言可能也被這樣對待。對於web開發,沒有Grails我不會知道我會怎樣編寫web應用,簡單來講這東西不用提刷新,還有點意思。Gradle是我工具箱中另一個寶貝,無論何時一有機會我就用它。最後,Spock展現了Groovy編譯器的強大,還有AST表達式經過一個簡單可是十分強大的測試DSL進行處理。」
Andres Almiray,
Groovy 提交貢獻者
Fantom
「Fantom是一個優雅的,強烈關注併發和可移植性的新一代語言。不可變性深深融入了Fantom 的類型系統,而且併發使用了演員(actor)模型。Fantom是着意於輕便性來設計的,而且對於Java VM和JavaScript/HTML都有產品質量級別的實現。Fantom務實於關注靜態類型的風格,可是也能輕易的容許進行動態編程。它是一種面嚮對象語言,但也包含了第一類函數(first class functions),而且許多的標準API都應用了閉包。」
Brian Frank,
Fantom創始人
Fantom入門
Fantom同咱們在這個報告中觀察的大部分其餘語言有一點點不一樣之處,它的目標是多平臺。基於JVM、.Net和JavaScript的編譯如今都已經提供支持了,而且鑑於他們已經就位的基礎設施,它應該也可能瞄準了其它的目標平臺。
可是儘管可移植性和平臺成熟度因素是Fantom做者(Brian 和 Andy Frank)考慮的重要問題,它也不是他們要定義這門語言的出處。他們聲稱Fantom是一個實用的語言,就是爲了幹實事的。
第一步要作的就是設置環境還有工具。幸運的是,Fantom使得這對於咱們來講很容易。Xored搞了一個稱做F4的基於Eclipse的IDE,它包含了咱們須要讓Fantom運行起來的一切。
Pod/腳本(Script)
Fantom能夠將文件做爲腳本執行,你只須要在文件中放置一個帶有main方法的類,而且有一個可執行的扇子(fan)就能夠運行它了。
1 |
class HelloWorldishScript |
4 |
static Void main() { echo( "Woah! Is it that easy?" ) } |
然而那不是構建Fantom程序的主要方法。對於大型的項目和提早編譯好模塊的產品級系統,稱做Pod,是使用Fantom的構建工具建立的。
構建是經過一個構建腳本編排的,它本質上是Fantom代碼的另外一塊。下面是HTTP服務器樣本Fantom實現的構建腳本:
02 |
class Build : build::BuildPod |
06 |
podName = "FantomHttpProject" |
08 |
srcDirs = [`./`, `fan/`] |
09 |
depends = [ "build 1.0" , "sys 1.0" , "util 1.0" , "concurrent |
這裏有幾個事項須要注意,例如依賴規範容許咱們用比jar包更少煩惱的方式,更加容易的構建更大型的系統。另外,一個Pod不僅僅只定義部署命名空間,還有類型命名空間,統一和簡化了二者。如今你能夠發現咱們的服務器依賴的Pod:sys、util和concurrent。
Fantom是如何出現支持多個後端(.net,JavaScript)的創意的?
「在過去的生涯中,咱們使用Java構建產品,可是有許多想要將解決方案賣到.NET商店中的問題。所以咱們設計了Fantom來同時針對兩個生態系統。而當咱們開始開發如今的產品時,咱們將Fantom轉入一個新的方向,目標是咱們運行在JVM上的後端使用一種語言和代碼庫,而咱們的前端運行在HTML5瀏覽器上面。多年來,這在目前已經成爲了一個很是成功的策略。」
Brian Frank,
Fantom創始人
標準庫/Elegance
Fantom的身份不只僅只是基於JVM平臺(或者事實上是任何其它的平臺)上的一種語言,而它自身更像就是處在JVM之上的一個平臺。平臺提供了API,而Fantom確保了API的精彩和優雅。在最基礎的層面上它提供了幾種文法,像下面這樣的:
3 |
Map map := [ 1 : "one" , 2 : "two" ] |
擁有一個週期文法( duration literal)是它的一個微不足道的小細節,可是當你想要設置一個延時操做的時候,你就能感受到好像已經有人幫你考慮到了這個場景。
IO的API涉及到一些基礎的類,如Buf、File、In/OutStreams,它們使用起來使人很愉悅。網絡互通功能也提供了,還有JSON的支持,DOM操做和圖形庫。重要的東西都爲你而存在着。Util這個pod也包含了一些頗有用的東西。代替一個擁有main方法的類,文件服務器擴展了AbstractMain類,而且能自由的傳遞參數、設置日誌。另外一個使用起來使人很愉悅的API是Fantom的併發框架,但咱們將只用幾分鐘來談論一下它。
互操做(Interop)
全部構建於JVM平臺之上的語言都提供了一些與原生Java代碼協同工做的能力。這對於利用Java的龐大生態系統起到了關鍵做用。也就是說,要建立一個比Java好的語言很容易,而建立一個比Java提供的至關得體的互操做更好的語言就難了。那部分歸因於對集合的處理(它有點了老舊且平淡,並且有時候使用起來有點兒痛苦)。
Fantom提供了一個Interop類,它有一個toFan方法和一個toJava方法,用於來回轉換類型。
2 |
InStream in := Interop.toFan(socket.getInputStream) |
3 |
OutStream out := Interop.toFan(socket.getOutputStream) |
這裏你能夠發現咱們有了原生的Java Socket,它天然的爲咱們提供了Java的Input和OutputStream。
使用Interop將他們轉換成與Fantom地位相同,而且稍後就使用它們。
靜態和動態類型?
另外一個任何語言都要審查的主題是這個語言是否提供靜態/動態類型的支持。
Fantom在這一點處在中庸的位置,而且咱們很喜歡這一點。屬性域(Field)和方法帶有強靜態了性的特性。可是對於本地變量,類型就是被推斷出來的。這致使了一種直觀的混合,方法約束是被拼湊出來的,可是你也並不須要給每同樣事物都賦上類型。
天然的,在Fantom中有兩種方法調用操做。點(.)調用須要經過編譯器檢查而且是強類型的,箭頭(->)調用操做則不是。這就從一個動態類型的語言中得到了鴨式類型(duck-typing)還有你想要的任何東西。
不可變性(Immutability)&併發(Concurrency)
Fantom提供了一個演員(Actor)框架來處理併發。消息傳遞和鏈式異步調用很容歸入代碼中。爲了建立一個演員(它將由某一種ActorPool支持,然後經過一個線程池得到),你須要擴展一個Actor類,而且重寫(奇怪的是你必須明明白白的給override關鍵詞框定類型)receive方法。
請注意避免在線程之間分享狀態,Fantom將堅持要你只傳遞不可變的消息給actor。不可變性在設計時就構建到了語言之中,所以你能夠構建全部的屬性域都是常量的類。編譯器將驗證爲actor準備的消息事實上是不可變的,不然將拋出一個異常。
比較酷,並且難於發現的一點就是,若是你真心須要一個可變的對象,你能夠把它封裝到一個Unsafe中(不,這不是那個不安全的概念,而是Fantom中的Unsafe).
2 |
socket := serverSocket.accept |
3 |
a := ServerActor(actorPool) |
稍後你能夠把原來的對象再取回來.
1 |
override Obj? receive(Obj? msg) { |
3 |
log.info( "Accepted a socket: $DateTime.now" ) |
4 |
Socket socket := ((Unsafe) msg).val |
----------------------------------------------------
"你應該考慮吧Unsafe做爲最後的殺手鐗——由於它會侵蝕Fantom中的整個併發模型.若是你須要傳遞可變的狀態 b/w Actor——你應該使用序列化——它是一個內建的特性: http://fantom.org/ doc/docLang/Actors.html#messages"
Andy Frank,
Fantom創始人
這意味着,適當的解決方案在這裏就會像這樣:
3 |
socket := serverSocket.accept |
5 |
a := ServerActor(actorPool, socket) |
7 |
a.send(「handleRequest」) |
這樣咱們就可以爲接下來爲對那個的IO操做存儲套接字對象(socket),而咱們不須要再去傳遞任何不可變的消息了.順便得到的好處是,這樣代碼看起來更好了.
函數(Functions)& 閉包(Closures)
Fantom是一種面向對象的語言,它像許多其餘的現代編程語言同樣,將函數作爲了頭等公民。下面的例子展現瞭如何建立一個Actor,咱們將含蓄指定一個接收函數向其發送幾回消息。
pool := ActorPool()
a := Actor(pool) |msg|
{
count := 1 + (Int)Actor.locals.get("count", 0)
Actor.locals["count"] = count
return count
}
100.times { a.send("ignored") }
echo("Count is now " + a.send("ignored").get)
Fantom’s的語法十分的友好,與其餘語言沒太大出入。咱們大概已經發現,它用":="來進行變量的賦值,這對我來講是一種災難(對這樣的小的語法細節,我並不十分喜歡)。然而IDE對此支持得十分友好,每當你犯此錯誤的時候,它都會提醒你。
一些小東東
在咱們研究Fantom的整過過程當中,一些促使這門語言更加棒的小東東會令咱們驚喜.例如,支持null的類:它們是可以聲明一個接受或者不接受null做爲參數的方法的一種類型.
經過這種方式,代碼不會受到空(null)檢查的污染,而且同Java的互操做變得更加的簡單了.
值得一提的還有另一種特性.那就是帶有變量插值(Variable interpolation)的多行字符串(Multi-line String):
2 |
"HTTP/ 1.1 $returnCode $status |
3 |
Server: Fantom HTTP Server 1.0 |
5 |
Content-type: ${contentType} |
6 |
Content-length: ${content.size} |
參數能夠有默認值,支持混合和聲明式編程,還有運算符重載.每同樣東西都各得其所.
只有一件東西咱們沒有看到並感到是一種缺陷,那就是元組(tuples).然而咱們只在想要多返回(mutilple return)時才須要那個東西,所以使用一個列表(list)就足夠了.
Clojure
「我着手建立一種語言,意在應對我在使用Java和C#編寫的一些類型的應用程序——像廣播自動化、調度以及選舉系統之類那些東西——它們許多都須要解決的併發問題.我發現只用面向對象編程和用那些語言的併發方法,對於處理這些類型的問題並不怎麼夠好——它們太難了。我是List的擁護者,還有其它的函數式語言,而我想要作的就是解決那些問題,創造一種立足於實際的語言,不再用拿Java來編程了.」
Rich Hickey,
Clojure創始人在2009年InfoQ訪談中
Clojure 入門
Clojure 第一次出如今2007年 。和一些成熟的語言相比,它相對是新的。Rich Hickey創造了它。它做爲Lisp方言在JVM上面。版本1.0出如今2009年。它的名字是一個雙關語在C(C#), L (Lisp) and J (Java)。當前Clojure的1.4。Clojure是開源的(發佈在Eclipse 公共許可證 v1.0—EPL)
當你開始閱讀Clojure的文檔的以後,咱們決定開始配置環境變量。咱們使用Leiningen(它是一個爲Clojure準備的構建工具) 。對Clojure來講,Leiningen (或者縮寫Lein) 可以執行大部分任務。它可以執行咱們期待的來自Maven相關的例如:
- 創造一個項目骨架 (想想: Maven 原型)
- 操做依賴
- 編譯 Clojure代碼到JVM classes類
- 運行測試
- 發佈構件到一箇中央倉庫
假如你使用過Maven, 你會感受使用Lein很是的溫馨。事實上,Lein 甚至支持Maven的依賴。然而二者的主要不一樣點在於 Lein的項目文件由 Clojure寫成。然而Maven使用XML(pom.xml)。儘管有可能去開發用Clojure沒有其它的,咱們不得不認可Lein是一個很是受歡迎的增長物。它真得是某些事情變得更加簡單。
開始獲得項目骨架,你可以作像下面這樣:
$ lein new clojure-http-server
集成開發環境(IDE)的支持
準備好基本的項目結構之後,就能夠開始編輯代碼了.若是你是Eclipse的長期使用者,你首先要作的一件事情就是找到一個能夠處理Clojure 的Eclipse插件.它應該在一個成熟的工做空間中提供語法高亮和代碼補全功能.幸運的是Eclipse中有了一個叫作CounterClockWise的Clojure插件.只要安裝了這個插件,就能夠在Eclipse中新建Clojure項目了.
這樣很不錯,咱們不須要再去管將咱們已經使用過的,前面章節提到的Lein,在命令行中建立的Clojure項目而後導入到Eclipse中這種麻煩事了。咱們預計CounterClockWise插件提供了像Eclipse的Maven插件有的在pom.xml和EclipseGUI之間兩種交互方式,所提供的功能.
僅僅爲了好玩,咱們也看了看Clooj,它是用Clojure自己開發的一個輕量級的Clojure IDE.下載和運行它都很容易,不過咱們發現它同Eclipse相比,就有點黯然失色了.
最後咱們用了用最難的方式開發Clojure程序——只在命令行中使用Lein,還有可靠的GVIM做爲文本編輯器——主要是想看看Lein是如何詳細工做的.
交互式解釋器(REPL)
像衆多的函數式語言, Clojure提供的命令行shell能夠直接執行Clojure語句。這個shell對開發者很是方便,由於它在開發中不只容許你測試一小端代碼,並且容許你運行程序的一部分。
這或許對用Python,Perl開發的碼農沒什麼新鮮的,但對Java開發者來講,這無疑帶來更新鮮、更交互的方式來寫代碼。
函數式編程——另一種思考方式
Clojure是一種很是相似於Lisp和Scheme的函數式編程語言.函數式範式同那些習慣於Java的面向對象方式而且習慣於其反作用的方式很是不一樣.
函數式編程推崇:
- 不多或者徹底沒有反作用
- 若是使用相同的參數區調用,函數就永遠返回同一個結果(而不是依賴於對象狀態的方法)
- 沒有全局變量
- 函數是第一位的對象
- 表達式懶計算(Lazy evaluation of expression)
這些特性並非Clojure獨有的,而是整體上對函數式編程都要求的:
1 |
(defn send-html-response |
3 |
[client-socket status title body] |
4 |
(let [html (str "<HTML><HEAD><TITLE>" |
5 |
title "</TITLE></HEAD><BODY>" body "</BODY></HTML>" )] |
6 |
(send-http-response client-socket status "text/html" |
7 |
(.getBytes html "UTF-8" )) |
同Java的互操做性
Clojure提供了優秀的同Java庫的互操做功能.事實上,對於一些基本的類,Clojure並無提供它本身的抽象,而是超乎你預期的直接使用了Java類來代替.在這個HTTP服務器的示例中,咱們從Java中獲取了像Reader和Writer這樣的類:
1 |
(ns clojure-http-server.core |
2 |
(:require [clojure.string]) |
3 |
(: import (java.net ServerSocket SocketException) |
5 |
(java.io PrintWriter BufferedReader InputStreamReader BufferedOutputStream))) |
建立和調用Java對象是很是直截了當的.而實際上有兩種形式(在這個優秀的Clojure介紹中描述到了):
1 |
(def calendar ( new GregorianCalendar 2008 Calendar/APRIL 16 )) ; |
3 |
(def calendar (GregorianCalendar. 2008 Calendar/APRIL 16 )) |
5 |
(. calendar add Calendar/MONTH 2 ) |
6 |
(. calendar get Calendar/MONTH) ; -> 5 |
7 |
(.add calendar Calendar/MONTH 2 ) |
8 |
(.get calendar Calendar/MONTH) ; -> 7 |
下面是一個實際的樣例:
2 |
"Create a Java reader from the input stream of the client socket" |
4 |
( new BufferedReader ( new InputStreamReader (.getInputStream client- |
然而,對於一些結構,咱們決定要使用到Clojure的方式.原生的Java代碼使用StringTokenizer,這樣作違背了不可變對象的純函數式原則,還有無反作用的原則.調用nextToken()方法不只有反作用(由於它修改了Tonkenize對象)而且使用同一個(或許是不存在的)參數也會有不一樣的返回結果.
因爲這個緣由,咱們使用Clojure的更加"函數式"的Split函數:
02 |
"Parse the HTTP request and decide what to do" |
04 |
(let [reader (get-reader client-socket) first-line |
05 |
(.readLine reader) tokens (clojure.string/split first-line # "\s+" )] |
06 |
(let [http-method (clojure.string/upper- case |
07 |
(get tokens 0 "unknown" ))] |
08 |
( if (or (= http-method "GET" ) (= http-method "HEAD" )) |
09 |
(let [file-requested-name (get tokens 1 "not-existing" ) |
併發(Concurrency)
Clojure從一開始設計對併發很上心,而不是過後諸葛亮.使用Clojure編寫多線程應用程序很是簡單,由於全部的函數默認都實現了來自Java的Runnable和Callable接口,自身得以容許其任何方法在一個不一樣的線程中運行。
Clojure也提供了其它特別爲併發而準備的結構,好比原子(atom)和代理(agent),可是咱們在這個HTTP服務器示例中並無使用它們,而是選擇熟悉的Java的Thread.
4 |
(.start ( new Thread (fn [] (respond-to-client client-socket))))) |
有關方法使用順序的問題
咱們認識到的一件事情是在源代碼文件中,方法的順序是嚴格的。函數必須在它們第一次被使用以前被定義。另一種選擇是,你能夠在一個函數被實際定義以前利用一種特殊的聲明格式,來使用函數。這讓咱們想起了C/C++的運做方式,它們使用頭文件和函數聲明。
Ceylon
"Ceylon由Java啓發產生。咱們試圖去創造一個更強大的語言。咱們時刻牢記不能作的更加糟糕比起Java - 那就是說,咱們不想打破某些Java作的好的地方或者咱們不想失去某些特徵。那就是在團隊環境中,Java可以編寫大的穩定的項目。"
Gavin King,
Ceylon 的語言的創造者
Ceylon 入門
做爲一個Java開發者, 你將會但願可以很是快地採用Ceylon。在項目的主頁上,開始編碼基本上至關於走Ceylon新的旅程 - 從那裏你將會獲得大部分關於語言的信息。
對於Ceylon,咱們首先要關注的是它精心準備的「基礎設施(infrastructural)」部分。在這裏基礎設施的意思是模塊。Ceylon運行時是基於JBoss的模塊(Module)的,而且Ceylon用模塊表明一切事物。即便是你新打造的項目實際上也是一個Ceylon模塊,它是經過項目文件夾裏面的一個module.ceylon文件來聲明的:
1 |
module com.zt '1.0.0' { |
2 |
import ceylon.interop.java '0.4.1' ; |
這裏的意思是這個模塊叫作com.zt,它的版本號是1.0.0。關於模塊聲明的格式須要注意:模塊名不須要在引號中,而模塊的版本號須要在引號中,好比'1.0.0'。這有點怪吧,由於版本號是一個數字而包不是的。爲何不省掉版本號的引號呢?多是由於版本號會包含非數字的字符吧,像'1.0.0-SNAPSHOT’。可是徹底省掉引號,Ceylon也可能理解。
Ceylon出色的踐行了模塊化,而且將運行時構建於Jboss模組。這些爲何如此重要?
首先,沒有模塊化,你就不能在不去破壞一些東西的前提下解決問題。
其次,沒有模塊化,你的程序就不能跨平臺運行。Java龐大的SDK意味着我不能肯定在一個JavaScript上面能跑Java程序。Ceylon"小巧"的語言模塊能在一個web瀏覽器上面工做得像在服務器上面同樣好。
第三,沒有模塊化,你就不能構建在模塊產品上做業的工具,也不能用模塊資源庫代替放置在你硬盤的單個的文件。
第四,沒有語言級別的模塊化,你就會助長像Maven和OSGI同樣的可怕的過分工程的技術。你也不得不在臃腫龐大的像JavaSE和JavaEE(JavaEE6以前)這樣的平臺上工做。
Gavin King
Ceylon創始人
其次,run.ceylon是項目的主文件,而咱們能夠實現run()方法來啓動咱們的應用:
2 |
print( "Hello from Ceylon!" ); |
Ceylon中的方法(或者函數)能夠是單獨的,而不屬於任何一個類。屬性(attribute)也一樣適用這一特性。實際上,發現它編譯成了什麼是至關有趣的一件事。每個單獨的屬性(或域)和方法都被編譯成了一個專用的類。那意味着下面的這段代碼被編譯成了兩個類,port_.class和run_.class——它們也一樣包含了公共靜態返回值爲空的main (public static void main)方法.
02 |
ServerSocket server = ServerSocket(port); |
03 |
print( "Listening for connections on port " port "..." ); |
05 |
Socket socket = server.accept(); |
06 |
print( "New client connection accepted!" ); |
07 |
HTTPServer httpServer = HTTPServer(socket); |
09 |
Thread threadRunner = Thread(httpServer); |
在這個過程當中咱們瞭解到,Ceylon工具編譯和打包了全部東西到Ceylon ARchive(.car)中,而且在那以後對自身作了清理,因此你不會在編譯以後找到class文件——咱們只有解壓這個壓縮了的存檔才能訪問class文件。
工具的一個很棒的部分是,當一個新的依賴被加入到module.ceylon文件中時,依賴會自動從CeylonHerd網站下載,而且由IDE處理識別。那是這個語言的基礎設施的另一個長處。
同Java的互操做性
同Java的互操做性對我來講是一個棘手的部分。首先,你必須將依賴添加到module.ceylon中,使得程序可以使用Java類:
當你在Ceylon中使用Java類時,你可能會直觀的指望着能夠像在Java程序中同樣正常的使用Java類的實例中的全部方法。可是在Ceylon中不是這樣的。下面是一個實例:
1 |
value printWriter = PrintWriter(socket.outputStream); |
你但願能在socket上面使用getOutputStream()方法,可是Ceylon將它解釋成了一個域的獲取器(getter),而且用一個屬性訪問修飾(大概是?)來取代它的語義。儘管當你檢查它的來源時,getOutputStream()並非一個獲取器。
類型轉換是咱們須要面對的第二個挑戰。Ceylon類型和Java類型之間有一種特殊的映射。
所以fileData必定是一個字節數組類型,而根據類型映射規則,它應該在Ceylon中被聲明爲一個Array:
1 |
Array fileData = arrayOfSize { |
2 |
size = fileLength; element = 0 ; |
噢,個人...(天),很差了:
1 |
Exception in thread "Thread-0" java.lang.ClassCastException: |
2 |
[J cannot be cast to [B |
3 |
at com.zt.HTTPServer.run(classes.ceylon: 59 ) |
4 |
at java.lang.Thread.run(Thread.java: 722 ) |
顯然,爲了讓類型轉換能正常的工做,咱們不得不 (在module.ceylon中)導入特殊的Java互操做模塊:
1 |
import ceylon.interop.java '0.4.1' |
而後纔有可能使用由互操做模塊提供的幫助方法。下面就是類型轉換所須要的代碼:
1 |
Array<Integer> fileData = createByteArray(fileLength); |
2 |
value fileIn = FileInputStream(file); |
"事實上這是一個真正使人痛苦的角落。咱們處理Java原生類型的通常性策略在處理器容器類型時是不健全的,這一般是很好的,由於Java的泛型並非基於Java原生類型的抽象:你不能擁有一個List。可是當Java有了數組(array)時,它們是特殊類型的容器類型,是基於原生類型的抽象,咱們所以須要用特殊的方法處理這種特殊的狀況。這個問題將會在將來版本的編譯器中被修復。"
Gavin King
Ceylon創始人
基本上,畢竟HTTP服務器實現的全部問題都已經在運行着了,可是在運行時會出現驚喜:null檢查。
如今應用程序啓動了,可是在客戶端提交了一個GET請求之後,它失敗了,打印以下的調用棧:
1 |
Exception in thread "Thread-1" java.lang.NullPointerException |
3 |
at com.redhat.ceylon.compiler.java.Util.checkNull(Util.java: 478 ) |
4 |
at com.zt.HTTPServer.run(classes.ceylon: 19 ) |
這個使人驚奇的部分位於classes.ceylon第19行,這裏有這樣一行代碼:
1 |
String input = bufferedReader.readLine(); |
這裏是一些如何使用javap幫助來閱讀反編譯代碼的知識。字節碼透露了Ceylon編譯器在Java類的方法返回以後插入了null檢查。所以要解決這個問題,咱們不得不在聲明中使用?後綴:
1 |
String? input = bufferedReader.readLine(); |
可是隨後就會有這個值是如何被使用的問題。例如,若是它是做爲StringTokenizer的一個參數使用,那麼初始化這個類應該也會失敗。所以就有了下面這種蹩腳的解決辦法:
確定應該有處理這種狀況的更好方式,而不是像眼前這樣就處理了問題。
"若是有你確實不想在null的狀況下再作一些什麼事情的場景,你能夠就寫「assert(exists input);",那就會將範圍縮小到非空的類型。」
Gavin King
Kotlin
「咱們認爲Kotlin的定位是一種現代化工業語言:它專一於代碼重用和可讀性的彈性抽象,以及面向早期錯誤偵測,和明確捕獲維護與清理的意圖,這些問題的靜態類型安全性。Kotlin最重要的使用場景之一是對於一個龐大的Java代碼庫,其開發者須要一個更棒的語言:你可以將Java和Kotlin自由混合,遷移能夠是漸進式的,不須要一會兒對整個代碼庫進行改變。」
Andrey Breslav
Kotlin創始人
考察Kotin時,做爲JetBrains創造的產品,咱們首先要面對的問題就是其餘IDE對它的支持。因爲咱們大多數人都是Eclipse的用戶,切換到IntelliJ IDEA環境老是困難的,可是若是你想使用Kotlin環境進行編碼工做,這老是免不了的一個步驟。安裝Kotlin插件式至關容易的,Kotlin的artifact也是,但使人難堪的就是支持沒有推廣到其它的IDE。也許咱們應該經過某種恰當的方式向JetBrains團隊就這個問題發出一些暗示才行?:)
優雅的編碼
使用Kotin進行編碼確實會生產出一些很是優雅的代碼。它消除的進行null檢查的必要,使用主構造器(primary constructors),智能的轉換(smart case),範圍表達式...這個清單還沒完呢。那就讓咱們先來看個例子吧。
以咱們Java的背景來看,咱們不喜歡使用when結構鑄造(casting)的is組合。在Java中,將它們看做是鑄造的(A)對象的實例,各自進行轉換。is的使用也將會引用一個鑄造(cast),若是你率先直接使用這個對象的話。好比:if(stream is Reader)stream.close()。在這個例子中,close方法在Reader接口上被調用了。這應該也能夠像這樣子表述:if(stream is Reader)(stream as Reader).close(),可是不須要額外的代碼了。這種使用when的組合容許你對變量進行切換,但當你可以得到一種更加豐富的參與時,就不只僅只使用它的值了。你能夠考慮像下面這樣:
2 |
is Reader -> stream.close() |
3 |
is Writer -> stream.close() |
4 |
is InputStream -> stream.close() |
5 |
is OutputStream -> stream.close() |
6 |
is Socket -> stream.close() |
7 |
else -> System.err.println( "Unable to close object: " + stream) |
若是你能想象你將會怎樣在Java中實現這個示例,你就會以爲上面的代碼有多幹淨優雅了。有趣的是C#也有相似的is和as的使用方式,一樣也實現了可爲空(nullable)類型。
利用參數命名(parameter naming)的方法調用也是默認的。這種問題也能在你須要調用一個擁有五個布爾值參數的方法,確保你傳入的true和false順序正確時,讓你感到頭痛。參數命名經過在方法調用上帶上參數名稱來繞過參數的混淆。這樣很棒。這在過去已經被其餘的語言和腳本框架再一次作到了,它們容許當用戶樂於接受一些默認參數值時,方法調用能夠省略掉某些參數。讓咱們來看一個例子:
1 |
private fun print(out : PrintWriter, |
3 |
contentType: String = 「text/html」, |
4 |
contentLength : Long = - 1 .toLong(), |
5 |
title : String, body : () -> Unit) |
這裏咱們有了一個帶有若干個String參數的方法,還有一個做爲輸入的函數。它使用下面的方式來調用。請注意contentType和contentLength參數都被省略了,意味着聲明中的默認值將被使用。
2 |
pre = "HTTP/1.0 404 Not Found" , |
3 |
title = "File Not Found" , |
1 |
404 File Not Found: " + |
那麼這有什麼意義呢?這將省掉不少方法重載!雖然言重了一點,可是一想到Java在過去超過20多年時間裏沒有像這樣的好東西出世,就足以讓這個功能使人驚奇了。有時候就有一點點感受你是在暗無天日的編寫着代碼。加點油吧,Java,你得奮起直追才行!
Kotlin將幫助你編寫安全的代碼,除非你不想這樣作
首先,你能夠對NPE(空指針異常)說再見了!Kotlin使用「可爲空(nullable)類型」和「不可爲空(non-nullable)類型來將可能會爲空的變量和永遠不會爲空變量進行區分。考慮下面的代碼:
爲了容許爲空,變量必須被聲明爲nullable,在這個場景中,就寫上 String?:
如今,若是你調用一個使用了變量a的方法,它會擔保不會出現NPE,所以你能夠安全的說:
可是若是你使用b調用同一個方法,那就不怎麼安全了,編譯器會報告說出了一個錯誤:
經過知曉哪些變量可爲空,Kotlin編譯器負責當你解綁一個可爲空的引用時,你能夠使用下面的方法其中之一來作到:
Kotlin中的安全調用同Groovy中那些功能很是類似,包括記號(notation)語法。經過像下面這個例子同樣使用‘.?’解綁一個可爲空類型,告訴編譯器調用對象b上面的length方法,而一旦b爲空,就不作這一步調用length的操做。
你考慮的那些」什麼?避免null?那樣作有啥樂趣?」,!!操做符容許拋出潛在的NPE,若是你但願這樣的話。
你也能夠使用?標記來避免掉假如轉換(cast)是不可能時,異常被拋出的狀況。這稱做一個安全轉換。
函數
函數能夠在(類)的內部建立(成員函數)或者在類以外。函數能夠包含其餘函數(局部函數),你能夠使用函數去擴展已經存在的類,像下面這樣:
fun Int.abs() : Int = if (this >= 0) this else -this
該例子擴展了Int類,返回絕對值。
函數功能很是強大,在JVM上用的很好(在Java裏仍然沒有,直到lambda表達式在Java8裏出現)。Kotlin 也容許使用更高的命令函數,也就意味着你能夠經過將一個函數做爲參數以讓方法進行調用(函數文本)。
文檔/幫助
咱們發現Kotlin的文檔都在同一個地方。全部的Google的搜索都指向了社區網站。這有點兒糟糕,由於有時候去一些地方找找其餘的示例和資源來看看也是不錯的,而它仍然是相對年輕的。若是有其餘人跟進,從而能在Github上面看到更多代碼的話,就行了。
Xtend
「Xtend是一種靜態類型編程語言,它能夠被翻譯成可讀的Java源代碼.它支持現存的Java風格,並且被設計得比Java自己同現存的Java API能工做得更加好。Xtend是一個構建實用抽象的強大和靈活的工具,而且配備了先進的EclipseIDE集成.它是Eclipse的一個開源項目.」
Sven Efftinge
Xtend創始人
Xtend自誇爲現代化的語言。它的速度比Groovy更快,比起Scala更簡單, 吸收了Java全部的好處。
如今有很好Xtend的Eclipse IDE開發工具,它是由Xtext語言寫成的。咱們不須要去問爲何,由於Xtend和Xtent都是Eclipse項目。其它的IDE fanboys(Anton)不會緊密的結合和選擇其餘的IDE,相似的Kotlin 寧肯選擇 IntelliJ IDEA。
各就各位,預備,寫代碼!(On your marks,Get set,code!)
咱們直接從Eclipse得到了一些代碼,發現它其實(對於Java開發者來講)至關的熟悉.Xtend的語法仍然同Java是同樣的,這能夠同時被看作是好事和壞事,取決於你對於Java語法的我的印象:)它更簡單了,通常的Java裝備(baggage)都去掉了(最新的lang也是),留下了更好的可讀性,而僅僅使用其結構(structure)和關鍵詞,留給咱們一個可讀性更好的,更加乾淨的實現.讓咱們來看看一些代碼:
3 |
def static void main(String[] args) |
5 |
println( 'Gangnam, Style' ) |
請注意咱們不喜歡用"hello world"這個例子,由於你不多從它們這樣的例子裏得到更多的東西,所以這裏使用咱們的"江南(Gangham)Style"示例:)到目前爲止咱們看到同Java相比極少有改變,但咱們須要注意到的是IDE中有一個xtend-gen源代碼文件夾,它裏面有從Xtend代碼翻譯的Java代碼:
2 |
import org.eclipse.xtext.xbase.lib.InputOutput; |
3 |
@SuppressWarnings ( "all" ) |
5 |
public static void main( final String[] args) { |
6 |
InputOutput.println( "Gangnam, Style" ); |
有趣的是它經過本身實現的InputOutput類來進行輸出。
回顧Xtend HTTP server 樣例, 咱們寫了137行代碼, 被翻譯成309行Java代碼。被建立的Java代碼不太可讀而且也不是很順利的編輯, 因此我不確信爲何這被顯示。
有趣的是, 在咱們開始編譯Xtend代碼的時候,Java代碼並無出現這種狀況 :)
爲何你決定要將你的Xtend代碼翻譯成Java代碼,而不是直接翻譯成字節碼?
擁有快速和優化了的Java編譯器,Xtend代碼通常能運行得和詳細的Java代碼同樣快.
到目前爲止,最大的好處是透明性.Xtend代碼翻譯成Java源代碼能讓你瞭解Xtend編譯器實際在乾的事情.Java是廣泛被瞭解和受到信任的,翻譯成它給了人們所須要的信心.這在當你是一個新手,並且想要學習代碼的一個特定片斷是如何被翻譯的,還有編譯器如何在覆蓋之下工做的,這些時候是頗有幫助的.你可以一直有Java代碼做參考.基於Eclipse的Xtend IDE甚至提供了所謂的"生成代碼視圖(Generated Code View)",它能夠對應顯示Xtend代碼和生成的Java代碼。你能夠經過選擇Xtend代碼的一個很是細粒度的部分發現Java代碼是如何生成的.
在調試期間,你也可以在Xtend調試和Java源代碼調試之間切換.Xtend生成了我所謂的"聲明化的(statementisized)"Java代碼.函數式編程典型的長長的鏈式表達式被翻譯成了帶有合成(synthetic)變量的多行語句.於是你可以逐一瀏覽單獨的表達式,而且查看其中間結果.
Sven Efftinge
Xtend創始人
你能用 Xtend 作哪些 Java 作不到的事情?
這是很是有趣的地方,由於Xtend提供了許多Java缺少的特性。例如,它提供了擴展的方法,導入的擴展,lambdas,數據集對象,隱式類型和更多。這些很棒! 可是你能夠在這個報告中所描述的許多其餘語言中列出幾乎全部這些。是什麼讓這門語言有所不一樣呢?在咱們看來,它不是試圖成爲不一樣,而是試圖成爲如今的Java和將來的Java之間差距的橋樑。它是打了激素的Java。
代碼段
已經說得夠多了!須要更多的圖片和代碼!讓咱們深刻語言的一些靈活部分,開始先看看擴展方法吧.
這裏咱們建立了一個新的方法,稱做sendHtmlResponse,它有四個參數,然而這裏有一個靈活的語法招數,容許第一個參數是咱們嘗試去擴展的對象.
01 |
def sendHtmlResponse(Socket socket, String status, String title, String body){ |
04 |
<HEAD><TITLE>«title»</TITLE></HEAD> |
09 |
socket.sendHttpResponse(status, "text/html" , html.toString. getBytes( "UTF-8" )) |
這意味着咱們實際上能夠在Socket對象上面調用sendHtmlResponse方法.
1 |
socket.sendHtmlResponse( |
2 |
"HTTP/1.0 404 Not Found" , |
4 |
"<H2>404 File Not Found: " + file.getPath() + "" ) |
固然在咱們的xtend-gen路徑中,咱們實際上生成的代碼像下面這樣:
1 |
this .sendHtmlResponse(socket, "HTTP/1.0 404 Not Found" , "File Not Found" , _plus_3); |
而這破壞了錯覺(illusion),可以去擴展類真的是很是棒的,這實際上就是Xtend名字的由來之處.
在咱們第一個代碼塊中,咱們使用了下面這種結構:
2 |
<HEAD><TITLE>«title»</TITLE></HEAD> |
這被稱做模板表達式,被用在了許多其餘的語言上.它容許咱們很容易的建立HTML,而不用去磕磕絆絆的使用out.print()語句,從而容許咱們更好的考到咱們建立的文檔是什麼樣的.你會注意到咱們能夠在語句中標記了變量,而且你也能夠在模板中使用條件控制結構.
咱們也會想要其餘語言中默認使用的命名參數,咱們堅持認爲無論如何,它都是全部語言的一部分!它(Xtend)確實使用了隱式類型,儘管這樣還不錯.語言自己仍然是靜態類型的,可是變量的類型是在編譯的時候由表達式將會賦予的值來決定的.例如:
2 |
val DEFAULT_FILE = "index.html" |
在這個例子中,開始的兩個變量都明確是String,而第三個則在一開始不能確認,所以咱們須要將它的類型聲明爲Socket。這就使得代碼同下面的Java代碼比起來更加的清楚乾淨:
1 |
private final String WEB_ROOT = "." ; |
2 |
private final String DEFAULT_FILE = "index.html" ;/br> private final Socket socket; |
更多些的語法差別帶來了更多的工做要作,像"咱們的類方法去哪兒啦!"噢,你須要使用一種不一樣的標記來作到——File::分割符。我不太肯定爲何要這樣作,但這不是一個大問題,所以不會對咱們形成太多困擾.
這裏有趣的事情是,IDE可能會針對一個問題抱怨許許多多的事情出來.這可能會令你至關泄氣,由於你不得不按照Xtend編譯器想要依循的順序去修復這些東西,而不是你想的順序.這方面Java工具仍然有很豐富的致勝經驗.
同Java的互操做性
許多語言有時候很難實現同Java的互操做,Xtend真的不想有它們那種問題,它們分享了一個近乎相同的類型系統,提供一個同這種語言簡單的往來映射.你可以從Xtend中調用Java,並且毫無疑問,反之亦然。這裏提到這些就夠了。
綜上所述
tl:dr(too long,didn't read)太長了,很差閱讀
這個報告已經很棒了——用一種真正極客(geeky)的方式,教育、挑戰和娛樂着!對咱們來講學習這些新的語言,而且嘗試去正視這些語言之間的區別,是頗有趣的.他們同Java有明顯至關大的不一樣,不可同日而語.更加新的JVM語言中閉包都明顯是重要的,而圍繞許許多多的關於如何處理null的問題,它們的觀點存在着一種有趣的分裂。下面是咱們所認爲的你應該對每一種語言要了解的事情:
Java仍然是活蹦亂跳的!咱們能夠預見lambda和伴隨的特性(防護方法,升級的集合庫)將很快在Java中產生重大影響.語法至關不錯並且一旦開發者意識到這些特性可以帶給他們生產力的價值,咱們將在將來看到許許多多利用這些優點的代碼.
咱們看到,Scala可以很是富有表現力,而且相比Java,它能夠容許咱們離開許多樣板語法。這是一個真正的優點,雖然咱們的HTTP服務器的例子可能表達不出來,但這也許能給你一些想法來了解這個語言是什麼樣子。關鍵的一點,就是擁抱不變性和嘗試象可被組成的表達式同樣塑造代碼。
做爲Java開發人員,Groovy很容易上手,並是Web應用程序,DSL,模板和腳本引擎的一個很棒的選擇。它處理了在Java中常見的煩惱,包裝了特性的現代集合,並有一個很棒的標準庫。Groovy會繼續被普遍採用, 是JVM的動態語言的一個很好選擇。咱們只是怕它在特徵集持續增加的同時失去焦點。
Fantom是一個成熟和穩定的編程語言有着很是優美的API。 咱們絕沒有說咱們已經完美了, 若是能夠使Fantom更好 ,你能夠改善它: 咱們 Github 倉庫對comments 和 pull requests開放。若是你只是觀其表面,你只能經過語法來判斷編程語言。 Fantom 有着類Java的語法,沒有分號而且會拋出了一些自身的關鍵字。不然, 這確定是一個來源於Java的優秀語法。
在JVM上面,Clojure 提供了強大函數式語言平臺上。若是開發有Lisp/Scheme經驗,那是很是爽的一種方式。然而,Java開發者不只僅是面對新的語法並且新的範式。
儘管它會被濫用相似像JAVA那樣,那不是最優的。在未來,Clojure 的強大之處在於併發方面會提供不少便利。愈來愈多的程序員都會感興趣代碼的並行之處。
Ceylon的語法和關鍵字對Java而言,很是的不同。然而,它不會困惑咱們多少。 在一些場景下面, Ceylon和Java相關性很強。對實例化而言, 'satisfies' 在Ceylon和 'implements' 在Java想對應。
總的來講,有一個至關有趣的經歷。咱們對Ceylon的印象是彙集OOP和基礎設施(模型系統)風格,而不是簡短的表達式。 不要嘗試百萬變成一行。Ceylon創造者擔憂Java語言的冗長,重點在於生態系統和語言設計。
總的來講,Kotlin 是一門友好的語言。儘管Kotlin沒有特別的創造性,它的優點在於吸取別語言最精華的部分。
該語言很是友好去使用,具備函數,參數名,默認值,擴展類等特色。 它也是一個漂亮的與語言! 關鍵字的比例:在Kotlin中,代碼在構造的時候例如,'when','is'和'as'更加好。
每一個2-3三月,發佈一個正式版本,Scala開發者可能會轉向去使用Kotlin這門語言。儘管咱們認爲Scala用戶不會太有興趣。
Xtent提供了Java只承諾在將來實現但目前缺少的簡潔明瞭的功能。問題是,咱們會建議讓別人在Java或其餘的JVM語言之上使用它嗎?
然而,對Java用戶而言這取決於這些好處能持續多久,一旦Xtend中附加的的這些這些不錯的功能在Java中像lambda和擴展的方法同樣,被實現了的話,咱們不相信有足夠的理由轉移到Xtend上來。咱們能想象使用其餘諸如Scala的JVM語言的開發者將Xtend看作Java n + 1或着帶有一種插件的Java,而不是一種打破了全部界限的語言,所以咱們不會理解爲何Scala的用戶可能會破天荒的去使用Xtend。