摘要:我們的目標(biāo)是建立對(duì)每一種語言的認(rèn)識(shí),它們是如何進(jìn)化的,未來將走向何方。有點(diǎn)的味道是堅(jiān)持使用動(dòng)態(tài)類型,但唯一還收到合理?yè)肀玫木幊陶Z言,然而一些在企業(yè)的大型團(tuán)隊(duì)中工作的開發(fā)者擇認(rèn)為這會(huì)是的一個(gè)缺陷。
為什么我們需要如此多的JVM語言?
在2013年你可以有50中JVM語言的選擇來用于你的下一個(gè)項(xiàng)目。盡管你可以說出一大打的名字,你會(huì)準(zhǔn)備為你的下一個(gè)項(xiàng)目選擇一種新的JVM語言么?
如今借助來自像Xtext和ANTLR這樣的工具的支持,創(chuàng)建一種新的語言比以前容易多了。個(gè)體編碼者和群體受突破和改進(jìn)現(xiàn)存JVM語言,以及傳統(tǒng)Java的限制和缺陷的驅(qū)使,讓許多新的JVM語言應(yīng)運(yùn)而生。
新的JVM語言開發(fā)者感覺他們的工作室針對(duì)現(xiàn)存語言的產(chǎn)物——現(xiàn)存的語言提供了太過受限制的功能,要不就是功能太過繁雜,導(dǎo)致語言的臃腫和復(fù)雜。軟件開發(fā)在一個(gè)廣闊的范圍被應(yīng)用,于是一種語言的有效性就決定于它跟特定任務(wù)領(lǐng)域的相關(guān)性,或者它如何在更廣泛的范圍中通用。所有這一切導(dǎo)致了資源庫(kù)和框架的開發(fā)。
大部分人大談特談JAVA語言,這對(duì)于我來說也許聽起來很奇怪,但是我無法不去在意。JVM才是Java生態(tài)系統(tǒng)的核心啊。
James Gosling,
Java編程語言的創(chuàng)造者 (2011, TheServerSide)
如此多的語言存世,語言的墳場(chǎng)是否會(huì)飽滿呢?這里有一個(gè)潛在的風(fēng)險(xiǎn),市面上可供使用的選擇太多,將會(huì)導(dǎo)致許多語言由于足夠的關(guān)注和社區(qū)貢獻(xiàn)而無法生存發(fā)展下去。
然而要在這個(gè)行業(yè)生存下去,必須基于創(chuàng)新和創(chuàng)造——這些往往來自于一個(gè)從零開始,并且放棄現(xiàn)有的抱怨和成見,在一塊白板上面起家的項(xiàng)目。
這里有一條我們將自然而然遵循的線索:現(xiàn)有的語言建設(shè)和框架幫助建造起來的社區(qū)支持了Java生存,并且也使得下一代Java和新的創(chuàng)意、結(jié)構(gòu)和范式,這些東西的產(chǎn)生成為可能,最終將使它們的方式體現(xiàn)在現(xiàn)存的語言當(dāng)中。
Rebel Labs的報(bào)道了概覽了Java 8,Scala,Kotlin,Ceylon,Xtend,Groovy,Clojure和Fantom。但是如此多的JVM語言可供選擇,我們?nèi)绾螘?huì)只看中這8種選擇?
Rebel Labs 的團(tuán)隊(duì)就如何出這樣一份報(bào)告,還有選擇哪種語言,進(jìn)行了六個(gè)月的討論。最基本的,我們想要呈現(xiàn)給每個(gè)人一些東西:Java是一種極其著名,應(yīng)用廣泛的語言,但是Java 8擁有許多我們想要一探究竟的新東西。Groovy,Scala和Clojure已經(jīng)找到了它們?cè)谑袌?chǎng)中的核心競(jìng)爭(zhēng)力,并且變得越來越流行起來,而像Ceylon,Kotlin,Xtend和Fantom在我們的涉獵中相對(duì)還比較新穎,需要經(jīng)受一些考察來獲得信任。
我們的目標(biāo)是建立對(duì)每一種語言的認(rèn)識(shí),它們是如何進(jìn)化的,未來將走向何方。因此在這份報(bào)告中,你將會(huì)看到我們闡述對(duì)于這些語言的第一印象,包括那些給我們帶來震撼的特性,以及不那么能打動(dòng)人的特性。
你將會(huì)看到一個(gè)HTTP服務(wù)器基礎(chǔ)實(shí)現(xiàn)的源代碼示例,它鏈接到了GitHub,因此你可以同我們一道來探討它。
一小段歷史
最開始只存在Java,它是用于在JVM上編程的唯一選擇。但是這個(gè)行業(yè)很早就滿足了對(duì)在JVM上面編程的更多和潛在的選擇需求。在腳本領(lǐng)域首先出現(xiàn)了Jython,JVM的一種Python實(shí)現(xiàn),還有Rhino和JavaScript的JVM引擎,它們出現(xiàn)在1997年,跟著就是2000年的BeanShell和2011年的JRuby。
由于對(duì)于動(dòng)態(tài)定制應(yīng)用程序的需喲,在那時(shí)腳本設(shè)施的需求很旺盛。如今,像Oracle WebLogic和IBM WebSphere這些應(yīng)用服務(wù)器都使用Jython腳本來執(zhí)行自動(dòng)化操作,而Rhino也被綁定在Java 6上面,使得JavaScript成了JVM上的一等公民。
jvm languages timeline
然而,腳本設(shè)施不是唯一一個(gè)讓基于JVM的可選編程語言滋生的因素。歸因于Java的向后兼容性原則,為了提供一些Java或者它的標(biāo)準(zhǔn)庫(kù)沒有提供的新穎特性,可選語言開始出現(xiàn)了。Scala和Groovy就是最先提供了一些東西超越了Java的成功項(xiàng)目.
我們可以觀察到一種有趣的現(xiàn)象:大部分新晉的編程語言都利用了靜態(tài)類型。使用Scala,Ceylon,Xtend,Kotlin和Java本身的開發(fā)者都依賴于在編譯時(shí)驗(yàn)證目標(biāo)類型。Fantom在動(dòng)態(tài)和靜態(tài)類型之間找到黃金的平衡中點(diǎn),而Groovy,盡管一開始是一種動(dòng)態(tài)語言,但是如今也在其2012年的2.0發(fā)行版中也開始加入編譯時(shí)的靜態(tài)類型檢查了。Clojure——有點(diǎn)Lisp的味道——是堅(jiān)持使用動(dòng)態(tài)類型,但唯一還收到合理?yè)肀玫腏VM編程語言,然而一些在企業(yè)的大型團(tuán)隊(duì)中工作的開發(fā)者擇認(rèn)為這會(huì)是Clojure的一個(gè)缺陷。
運(yùn)行在JVM上的新的編程語言,已經(jīng)有從定制化應(yīng)用程序的動(dòng)態(tài)腳本語言,向著一般意義的靜態(tài)的應(yīng)用程序開發(fā)語言改變的趨勢(shì)。
Java仍然是最常使用在JVM上的編程語言,而隨著Java 8發(fā)行版的到來,Java將嘗試在語法美學(xué)和多核編程方面,跟上時(shí)代的潮流。
在 Github Repo 上代碼樣例
在幾個(gè)JVM語言的引擎下這會(huì)變的很geek。 在這篇文章中,我們從新的角度看Java(換句話說, 在Java 8中), Scala, Groovy, Fantom, Clojure, Ceylon, Kotlin 和Xtend–mostly, 并且給出最吸引我們和我們最深刻的印象。
每一個(gè)語言都有自己的 HTTPServer 樣例 ,它們都在 github 上。你可以檢查我們的代碼,所有在這篇文章的JVM 語言 都在這:
github image
https://github.com/zeroturnaround/jvm-languages-report
JAVA 8
“我真正關(guān)心的是Java虛擬機(jī)的概念,因?yàn)槭撬阉械臇|西都聯(lián)系在了一起;是它造就了Java語言;是它使得事物能在所有的異構(gòu)平臺(tái)上得到運(yùn)行;也還是它使得所有類型的語言能夠共存?!?/p>
James Gosling,
Java編程語言的創(chuàng)造者 (2011, ServerSide)
Java 8 入門
JavaSE 8.0是值得期待的。
讓我們來看一看Java平臺(tái)的總體演變策略:
不去打破二進(jìn)制代碼的兼容性
避免引入源代碼級(jí)的不兼容
管控行為方式級(jí)別的兼容性變更
簡(jiǎn)單來說,目標(biāo)就是保持現(xiàn)有的二進(jìn)制文檔能夠鏈接并運(yùn)行,并且保持現(xiàn)有的源代碼編譯能夠通過.向后兼容的政策已經(jīng)影響到了Java這種語言的特性集,同時(shí)也影響到了這些特性如何被實(shí)現(xiàn).例如,使用目前的Java特性不可能促進(jìn)API的進(jìn)化,因?yàn)樽兏涌诳赡軙?huì)打破現(xiàn)有依賴于JDK接口的資源庫(kù),其源代碼的兼容性.這就產(chǎn)生了一個(gè)同時(shí)影響到語言和JVM的改變.
隨著Jigsaw——模塊化的主題——正從Java 8中取締,Lambda項(xiàng)目成為了即將到來的發(fā)行版中最重要的主題.盡管其名號(hào)有一點(diǎn)點(diǎn)誤導(dǎo)性.但是lambada表達(dá)式確實(shí)是其一個(gè)重要的部分,它本身并不是什么重要的特性,但卻是Java在多核心領(lǐng)域要做出努力的一個(gè)工具.
這個(gè)多核心的時(shí)代有對(duì)于并行庫(kù)的需求,并且也對(duì)Java中的集合(Collection)API造成了壓力.這下就需要lambda表達(dá)式使得API更加友好和易于使用.防御式方法是API革命的工具,并且也是現(xiàn)存的集合庫(kù)將如何向支持多核邁出步伐的基礎(chǔ).
那么你想要使用lambda,嗯?
如果你熟悉其它包含lambda表達(dá)式的語言,像Groovy或者Ruby,你將會(huì)驚喜與它在Java中是如此簡(jiǎn)單.在Java中,lambda表達(dá)式的作用表現(xiàn)在"SAM類型"——一個(gè)擁有抽象方法的接口(是的,接口現(xiàn)在可以包含非抽象的方法了——叫做防御方法)。
那么舉個(gè)例子來說,著名的的Runnable接口可以完美地適合于作為一個(gè)SAM類型提供出來:
Runnable r = ()-> System.out.println("hello lambda!");
這也同樣適用于Comparable接口:
Comparatorcmp = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0);
同樣也可以像下面這樣寫:
Comparatorcmp = (x, y) -> { return (x < y) ? -1 : ((x > y) ? 1 : 0); };
這樣就看起來似乎像是一行l(wèi)ambda表達(dá)式擁有隱式地語句返回了.
如果你想寫一個(gè)能夠接受lambda表達(dá)式作為參數(shù)的方法該怎么做呢?那么你應(yīng)該將這個(gè)參數(shù)聲明為一個(gè)功能的接口,然后你就能夠把lambda傳進(jìn)去了。
1 interface Action { 2 void run(String param); 3 } 4 public void execute(Action action){ 5 action.run(); 6 }
一旦我們擁有了一個(gè)將功能接口作為參數(shù)的方法,我們就可以像下面這樣來調(diào)用它了:
1 execute((String s) -> System.out.println(s));
同樣的表達(dá)式可以用一個(gè)方法引用來替換,因?yàn)樗皇且粋€(gè)使用了相同參數(shù)方法調(diào)用。
1 execute(System.out::println);
然而,如果在參數(shù)在進(jìn)行著任何變化,我們就不能使用方法引用,而只能使用完整的lambda表達(dá)式了:
1 execute((String s) -> System.out.println("" + s + ""));
這里的語法是相當(dāng)漂亮的,盡管Java本身沒有功能(functional)類型,但是現(xiàn)在我們已經(jīng)擁有了一個(gè)優(yōu)雅的Java語言的lambda解決方案。
JDK 8中的函數(shù)型(Functional)接口
如我們所了解到的,一個(gè)lambda在運(yùn)行時(shí)的表現(xiàn)是一個(gè)函數(shù)型接口(或者說是一個(gè)“SAM類型”),一種只擁有僅僅一個(gè)抽象方法的接口。并且盡管JDK已經(jīng)包含了大量的接口,像Runnable和Comparable——符合這一標(biāo)準(zhǔn),對(duì)于API的革命來說還是明顯不夠用的。而在整個(gè)代碼中大量使用Runnable,也可能不怎么符合邏輯。
JDK 8中有一個(gè)新的包——java.util.function——包含了許多應(yīng)用于新型API中的函數(shù)型接口。我們不會(huì)在這里將它們?nèi)苛谐鰜怼阕约河信d趣的話就去學(xué)習(xí)學(xué)習(xí)這個(gè)包吧:)
由于一些接口的此消彼長(zhǎng),看起來這個(gè)資源庫(kù)正在積極的進(jìn)化中。例如,它曾經(jīng)提供了 java.util.function.Block類,但是在我們寫下這份報(bào)告時(shí),這個(gè)類型卻沒有出現(xiàn)在最新的構(gòu)建版中了:
1 anton$ java -version
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)
如我們所發(fā)現(xiàn)的,它已經(jīng)被Consumer接口替代了,并且被應(yīng)用于集合資源庫(kù)中的所有新方法中。例如,Collection接口中像下面這樣定義了 forEach 方法:
1 public default void forEach(Consumer consumer)
2 for (T t : this) {
3 consumer.accept(t);
4 }
5 }
Consumer 接口的有趣之處在于,它實(shí)際上定義了一個(gè)抽象方法——accept(T t),還有一個(gè)防御型的方法—— Consumer chain(Consumer consumer).。這意味著使用這個(gè)接口進(jìn)行鏈?zhǔn)秸{(diào)用是可能的。我們還沒有在JDK的資源庫(kù)中找到 chain(...) 方法,因此還不怎么確定它將怎樣被應(yīng)用。
而且,請(qǐng)注意所有的接口都標(biāo)記上了@FunctionalInterface(http://download.java.net/jdk8/docs/api/java/lang/FunctionalInterface.html)運(yùn)行時(shí)注解。但是除了它在運(yùn)行時(shí)通過注解用javac去確認(rèn)是否真的是一個(gè)功能型接口以外,它里面就不能有更多的抽象方法了。
因此,如果你編譯下面這段代碼:
1 @FunctionalInterface
2 interface Action {
3 void run(String param);
4 void stop(String param);
5 }
編譯器將會(huì)告訴你:
1 java: Unexpected @FunctionalInterface annotation
2 Action is not a functional interface
3 multiple non-overriding abstract methods found in interface Action
而下面這段代碼將會(huì)正常的編譯:
1 @FunctionalInterface
2 interface Action {
3 void run(String param);
4 default void stop(String param){}
5 }
防御方法
出現(xiàn)在了 Java 8 中的一個(gè)新概念是接口中的默認(rèn)方法。它意味著,接口不僅可以聲明方法的簽名,也還可以保持默認(rèn)的實(shí)現(xiàn)。對(duì)于這個(gè)功能的需求源于對(duì)于JDK API中的接口進(jìn)化需要。
防御方法最顯著的應(yīng)用是在Java的Collection API。如果你使用過Groovy,你可能寫過像下面這樣的代碼:
1 [1, 2, 3, 4, 5, 6].each { println it }
而如今,我們使用像下面這樣的for-each循環(huán)來進(jìn)行迭代操作:
1 for(Object item: list) {
2 System.out.println(item);
3 }
能夠使用這個(gè)循環(huán)可能是因?yàn)?java.util.Collection 接口擴(kuò)展了 java.util.Iterable 接口,這個(gè)java.util.Iterable接口只定義了一個(gè)Iterator iterator()方法。要是想Java利用Groovy類型的迭代,我們就需要Collection和Iterable中都有一個(gè)新的方法。然而,如果添加了這個(gè)方法,就將打破現(xiàn)有集合資源庫(kù)的源代碼級(jí)別的向后兼容性。因此,在Java 8中,java.util.Iterable 添加了forEach方法,并且為它提供了默認(rèn)的實(shí)現(xiàn)。
1 public interface Iterable
2 Iterator iterator();
3 public default void forEach(Consumer consumer) {
4 for (T t : this) {
5 consumer.accept(t);
6 }
7 }
8 }
添加新的默認(rèn)方法并沒有打破源碼級(jí)別的兼容性,因?yàn)榻涌诘膶?shí)現(xiàn)類并不需要提供它們自己對(duì)于這個(gè)方法的實(shí)現(xiàn),因此從Java 7切換到Java 8以后,現(xiàn)有的代碼還能繼續(xù)通過編譯。如此,在Java8我們能夠像下面這樣編寫循環(huán)代碼:
1 list.forEach(System.out::println);
forEach方法利用了一個(gè)功能性接口作為參數(shù),因而我們能夠?qū)⒁粋€(gè)lambda表達(dá)式作為一個(gè)參數(shù)傳遞進(jìn)去,也或者可以像上面的代碼示例一樣是一個(gè)方法引用.
這種方法對(duì)于多核場(chǎng)景的支持是很重要的,因?yàn)槭褂眠@種方式你自信的忽略掉循環(huán)的細(xì)節(jié)原理而專注于滿足真正的需求——你所依賴的資源庫(kù)幫助你打理了循環(huán)的細(xì)節(jié)。新的lambda表達(dá)式本身對(duì)Java開發(fā)者并沒有多少意義可言,因?yàn)闆]有集合資源庫(kù)合適的API,使用lambda表達(dá)式不太可能能夠充分的滿足開發(fā)者們.
我們?cè)儐柫瞬煌琂VM語言的創(chuàng)建者和項(xiàng)目領(lǐng)導(dǎo)人對(duì)于Java8中新特性的看法
SVEN EFFTINGE——XTEND
是的,它們是完全必要的,并且是朝著正確方向的一個(gè)良好開端.Xtend將使用Java8作為可選的編譯目標(biāo),從何生成的代碼得到改善.
Java8的lambda表達(dá)式同Xtend中的lambda表達(dá)式在語義上非常類似.新的流API能夠毫無麻煩的同Xtend良好工作.事實(shí)上,它在Xtend上比在Java8上面工作得更好.關(guān)于這個(gè)我已經(jīng)寫了一篇文章[http://blog.efftinge.de/2012/12/java-8-vs-xtend. html]:-).相較于Java8的流(stream)API,我仍然更傾向于選擇Guava API,因?yàn)樗鼈兏臃奖愣铱勺x性更高。
防御方法也是一個(gè)不錯(cuò)的東西,盡管我不喜歡使用"default"關(guān)鍵字這種語法.他們掙扎過接口和類方法不同的默認(rèn)可見性.我想他們是在嘗試獲得一種明顯的語法上的區(qū)別,以便人們不再混淆類和接口.Xtend中雷和接口的默認(rèn)可見性是相同的,在這兒那將不是問題.
BRIAN FRANK – FANTOM
可能挺激動(dòng)的:-) 多年以后,如今實(shí)際上每一個(gè)現(xiàn)代的語言都已經(jīng)有了基本的函數(shù)式編程機(jī)制.然而仍舊有大量的使用著Java的程序員不知道這些概念的,因此我想向更多的開發(fā)者灌輸更多的函數(shù)式編程風(fēng)格將會(huì)是有益的.我們不認(rèn)為函數(shù)式編程時(shí)靈丹妙藥,但是確實(shí)是工具箱中很給力的一種工具.
GAVIN KING – CEYLON
Java8在某種程度上重新點(diǎn)燃了開發(fā)者對(duì)Java平臺(tái)的興趣,使他們回歸Java,那對(duì)于Ceylon和其他基于JVM的語言來說是非常美好的事情.
Java8使得一大堆常規(guī)的編程任務(wù)執(zhí)行起來更加的方便.當(dāng)時(shí),從另外一方面來看,經(jīng)過多年的嘗試,Java SE的團(tuán)隊(duì)仍然沒有推出內(nèi)置的模塊化功能,這令我極其失望.他們?cè)贘ava8上所作的良好工作絕對(duì)值得贊揚(yáng),然而失足于這樣一個(gè)關(guān)鍵之處,給予同樣的批評(píng),我想才是公平的.Lambda是使用和方便的語法糖.但是模塊化才是有關(guān)Java一切的關(guān)鍵之處,并且是它在某些領(lǐng)域失敗的關(guān)鍵原因.
JOCHEN THEODOROU – GROOVY
說我不興奮,確實(shí).里面有很多Groovy已經(jīng)實(shí)踐了很多年的東西.防御方法在我看來就像個(gè)半拉子步調(diào),還有就是我不怎么喜歡這樣使接口 混淆.Lambda表達(dá)式對(duì)我來說更加有意思,但是我發(fā)現(xiàn)Groovy的閉包(Closure)是更加強(qiáng)大的概念。Lambda表達(dá)式確實(shí)能夠使Java成為一門更好的語言,但是它們也會(huì)使得Java的一些明智的概念復(fù)雜化.我想當(dāng)確定,如果沒有Scala和Groovy的話,這些特性也許永遠(yuǎn)不會(huì)出現(xiàn).它們(指的是這些特性)是Java對(duì)來自眾多有競(jìng)爭(zhēng)力的其它可選JVM語言的壓力而做出的反應(yīng)。而且,它們也不得不在保持領(lǐng)頭羊地位和吸引高級(jí)用戶之間保持復(fù)雜的平衡.因而它們被困在了中間的某個(gè)地方,隨之產(chǎn)生了lambda表達(dá)式和防御方法.
GUILLAUME LAFORGE – GROOVY
這里我并不像Jochen那樣消極,盡管在其他語言如何影響Java朝那條路發(fā)展這一點(diǎn)上,他的觀點(diǎn)同實(shí)際情況相差并不遠(yuǎn).
雖然Java8的lambda表達(dá)式、推倒重來的"流(Stream)"式集合或者防御方法實(shí)際上并不如我們所想象的那樣,但是我認(rèn)為所有那些東西結(jié)合起來應(yīng)該能夠給開發(fā)者們進(jìn)化他們的API帶來一些新的東西,并且它應(yīng)該有希望更好的設(shè)計(jì)和精簡(jiǎn)新老框架的使用.所以我想,總體觀之,這對(duì)于Java來說是好事.
ANDREY BRASLAV – KOTLIN
Java變得更好意味著千萬開發(fā)者變得更加快樂.Kotlin能夠使他們中的一些人更加的快樂,但這是另外一碼子事了:)
只需要"使用閉包(clusure)的Java"的人們將會(huì)在Java8中獲得它(指閉包),并且會(huì)很高興。但是還有另外一些人,他們不僅僅只需要匿名函數(shù)(那也確實(shí)是非常重要的,但是整個(gè)世界可不止于此哦).
Scala
“意在使其端正,而不塞入太多的語言特性到其里面,我在Scala上專注于使它變得更加的簡(jiǎn)單.那是人們常常有的一種誤解,他們認(rèn)為Scala是一種帶有許許多多特性的宏大語言.盡管這通常不是真的.它實(shí)際上是一個(gè)相當(dāng)小的語言——當(dāng)Java8面世之時(shí)它將比Java更加的小巧。”
Martin Odersky,
Scala 創(chuàng)始人
Scala入門
同本報(bào)告中涵蓋的大部分語言相比,Scala是相當(dāng)行之有效的.2003年已經(jīng)有了它的第一個(gè)發(fā)行版,但是從2006年才開始出現(xiàn)在許多雷達(dá)(radar)上,當(dāng)時(shí)其2.0版本在EPFL上發(fā)行了.從那時(shí)起它就日益普及,并且有可能接近于與一線語言為伍了,你可以參考語言排行榜( language ranking )來確信這一點(diǎn).
從2006年開始,它的許多表現(xiàn)令其變得有趣起來——就是使用類型推斷混合了面向?qū)ο缶幊蹋∣OP)和函數(shù)式編程(FP:Funcitional Programming)的一種靜態(tài)類型;雖然不是原生的,但是被編譯成了高效的代碼。它擁有模式匹配、帶底層類型的先進(jìn)類型系統(tǒng)、代數(shù)數(shù)據(jù)類型、結(jié)構(gòu)類型甚至依賴類型。它也使得表達(dá)像單子(monad)這樣的分類理論抽象變?yōu)榭赡?,但是你可以在自己心里決定是否去在意這個(gè)東西。及時(shí)你在標(biāo)準(zhǔn)庫(kù)中使用了一些單子,但是甚至你也能夠在不知道單子是什么的時(shí)候那樣做。
世上可沒有什么典型的Scala開發(fā)者這一說——一些使用Scala的人是Java開發(fā)者,他們想要擁有更具表達(dá)能力的語言,還有一些是函數(shù)式編程者,他們發(fā)現(xiàn)了這是一種在JVM上使用的上佳語言。這意味著Scala程序能夠被編寫成許多完全不同的風(fēng)格——純函數(shù)式風(fēng)格的,勢(shì)必不純函數(shù)式的,或者兩者的混合風(fēng)格。你甚至可以交叉使用這些風(fēng)格,使用盡可能抽象的方式,利用先進(jìn)的類型系統(tǒng)(見 Scalaz&Shapless 資源庫(kù),或者只比在你會(huì)在Java代碼中使用的更多一點(diǎn)點(diǎn)的抽象。
從2006開始,Scala已經(jīng)經(jīng)歷了一些重大的變化。其中最大的一個(gè)變化是Scala2.8中經(jīng)過大大調(diào)整的集合API,它可能是任何語言中最強(qiáng)大的了,但是相比于大多數(shù)的集合庫(kù),其實(shí)現(xiàn)細(xì)節(jié)也具有更多的復(fù)雜性。版本2.9添加了并行集合(parallel collection),而版本2.10帶來了一大堆特性,其中一些是實(shí)驗(yàn)性質(zhì)的:衛(wèi)生宏( hygienic macros)、新的反射庫(kù)、字符串差值(String interpolation)、大大增強(qiáng)的模式匹配代碼生成器,還有更多的其它特性。
同Java的主要區(qū)別
在使用Scala實(shí)現(xiàn)的HTTP服務(wù)器(HTTPServer)樣本中,我們能夠馬上注意到它摒棄了static關(guān)鍵字,而不像Java中(這個(gè)HTTP服務(wù)器)將會(huì)是一個(gè)伴隨/單例(Companion/Singleton)對(duì)象中的靜態(tài)成員。一個(gè)伴隨(companion)對(duì)象是一個(gè)與類同名的對(duì)象。所以實(shí)際上,我們將我們的HttpServer切分成了一個(gè)對(duì)象和一個(gè)類——對(duì)象擁有靜態(tài)的部分,而類擁有動(dòng)態(tài)的部分。這些東西在Scala中是兩個(gè)不同的命名空間,但是為了方便我們能夠引入動(dòng)態(tài)部分中的靜態(tài)命名空間:
1 mport HttpServer._ // import statics from companion object
Scala允許在任何地方使用引入(import)語句,你可以從對(duì)當(dāng)前范圍可見的擁有成員的任何地方,將成員引入到當(dāng)前范圍之中;因此Scala代碼中的這個(gè)結(jié)構(gòu)(指import)時(shí)間上比Java更加的常用,而Java只允許你將引入語句放在文件的開頭,并且只能引入類或者類的靜態(tài)成員。
與Java比較,還有一個(gè)鮮明出眾的地方,那就是方法使用def name(argument:Type)的形式定義,并且變量也是如此定義:
1 val name: Type = initializer // final “variable”
或者是:
1 var name: Type = initializer // mutable variable
你應(yīng)該不會(huì)去選擇使用可變的變量,所以默認(rèn)使用val吧——我們的樣本代碼中并沒有任何使用var的定義,因?yàn)閷?shí)際上如果你過去大多是寫的Java代碼,那么它們比你所想象的有更少的使用機(jī)會(huì)。你常??梢宰岊愋吐暶鬟h(yuǎn)離定義,并且讓它自己去推斷,但是方法的參數(shù)必須總是有明確的類型。
從main()方法可以看出,從Scala調(diào)用Java代碼常常是很容易的,它通過Executors類創(chuàng)建了一些像ServerSocket這樣的一些Java對(duì)象。
Case類和模式匹配
Scala中比較有趣的一個(gè)特性是case類,它像是一種普通的類,但是帶有編譯器生成的equals、hashCode、toString、支持模式匹配的方法等等。這讓我們可以用很少的幾行代碼創(chuàng)造保存數(shù)據(jù)的小型類型。例如,我們選擇使用一個(gè)case類保存HTTP狀態(tài)行的信息:
1 case class Status(code: Int, text: String)
在run()方法中,我們能夠看到一個(gè)模式匹配的表達(dá)式。它同Java中的switch類似,但是更加強(qiáng)大。然而,這里我們不去深入了解它真正的強(qiáng)大之處,我們只使用一個(gè) “|(或運(yùn)算)”模式,還有你能夠通過使用 name @的前綴 將一個(gè)匹配結(jié)果綁定到一個(gè)名字(name) .
我們使用的模式是要去匹配一個(gè)從HTTP連接輸入流讀取的HTTP方法名。第一次的模式中我們匹配“GET”或者“HEAD”,第二次則是其它的任何東西:
1 case method @ ("GET" | "HEAD") =>
2 ...
3 case method =>
4 respondWithHtml(
5 Status(501, "Not Implemented"),
6 title = "501 Not Implemented",
7 body =
另外一個(gè)有趣的特性——它在Scala2.10中被加入——它是String差值。你在編寫常規(guī)的String常量時(shí)帶上s前綴,它就允許你在String中嵌入Scala代碼,使用${}或者$來使得簡(jiǎn)單名字區(qū)分識(shí)別出來。同多行的String結(jié)合起來,我們能容易的構(gòu)造出將被發(fā)送的HTTP頭部,不帶任何String的串聯(lián)操作:
1 val header = s"""
2 |HTTP/1.1 ${status.code} ${status.text}
3 |Server: Scala HTTP Server 1.0
4 |Date: ${new Date()}
5 |Content-type: ${contentType}
6 |Content-length: ${content.length}
7
8 """.trim.stripMargin + LineSep + LineSep
(注意:我們可以選擇丟棄零參數(shù)方法的括號(hào)。)
Scala也允許我們實(shí)現(xiàn)我們自己的String插值,但是這里用默認(rèn)實(shí)現(xiàn)的已經(jīng)足夠了。
trim方法是通常的Java的String.trim()方法,它用于從頭到尾去掉字符串中的空格字符(也包括換行符)。stripMargin方法則將去掉字符串每一行從開始直到 | (尾部連結(jié))符號(hào)的所有東西,并且允許我們?cè)诙嘈凶址厦嬲J褂每s進(jìn)。這個(gè)方法通過從String到WrappedString的隱式轉(zhuǎn)換被添加到String類型中,并且如法炮制,我們可以添加我們自己的邊緣剝離(margin stripping)邏輯,例如做到讓你可以在沒有額外的 | 字符的前提下,對(duì)每一行執(zhí)行trim操作。
內(nèi)置XMl,愛它還是恨它
在respondWithHtml方法中,我們看到另外一個(gè)有趣但不那么可愛的Scala特性:內(nèi)置XML表達(dá)式。該方法用一系列的XML節(jié)點(diǎn)(scala.xml.NodeSeq),以XHTML子元素形式,作為輸入?yún)?shù),然后將它們包裹于另一個(gè)XML表達(dá)式之中,在這些實(shí)際的title和body周圍增加了HTML/HEAD/BODY元素,再將它轉(zhuǎn)換為字節(jié)。我們調(diào)用這個(gè)方法時(shí),我們可以為body提供XML表達(dá)式形式的元素。
def respondWithHtml(status: Status, title: String, body: xml.
NodeSeq) =
...
避免空指針錯(cuò)誤
在toFile和sendFile中,我們使用Scala處理可選值的首選方法,選擇類型(請(qǐng)注意:Scala也有空值)。toFile會(huì)返回一些(文件)或者如果沒找到服務(wù)的文件則不反悔文件,然后sendFile會(huì)做一個(gè)涵蓋兩種情況的模式匹配。如果我們遺漏了任何一種情況,編譯器都將警告我們?cè)撉闆r。
1 def toFile(file: File, isRetry: Boolean = false): Option[File] =
2 if (file.isDirectory && !isRetry)
3 toFile(new File(file, DefaultFile), true)
4 else if (file.isFile)
5 Some(file)
6 else
7 None
所有東西都是表達(dá)式
我們也能利用這一事實(shí)——那就是在Scala中幾乎所有的構(gòu)造都是表達(dá)式——于是像if-else-if-else這種控制結(jié)構(gòu)實(shí)際上就產(chǎn)生一個(gè)值。因此我們能夠省略掉括弧,直接讓方法使用一個(gè)表達(dá)式。
在sendFile方法中我們可以見到更多的這種東西,在里面我們使用了本地的{...}塊,它產(chǎn)生了一個(gè)值——這個(gè)塊的最后一行表達(dá)式是它的返回值,如此我們隱藏了塊中的臨時(shí)變量,并且將結(jié)果分配到塊的最小的一個(gè)臨時(shí)變量中。
1 val contentType = {
2 val fileExt = file.getName.split(".").lastOption getOrElse ""
3 getContentType(fileExt)
4 }
盡管這里沒有展示出Scala完整的深度,但是我們看出它具有的表現(xiàn)能力,而且這個(gè)例子如期提供了關(guān)于這個(gè)語言是什么的一些觀點(diǎn)。關(guān)鍵是擁抱不變性,并且嘗試將代碼組合建模成表達(dá)式。因此相較于我們提供的樣本,toFile方法似乎最好從sendFile中提取出來。
Groovy
“Groovy有超過Java將能夠提供的甜點(diǎn),例如它具有輕易地在宿主程序中嵌入并編譯,以提供定制業(yè)務(wù)規(guī)則的能力,還有它如何為領(lǐng)域特定語言(Domain-Specific Language)提供優(yōu)雅,簡(jiǎn)潔并且可讀性好的語法的能力.”
Guillaume Laforge,
Groovy的項(xiàng)目帶頭人
Groovy入門
Groovy并不像我們?cè)谶@個(gè)報(bào)告中涵蓋的一些語言那樣具有冒險(xiǎn)性質(zhì),但絕對(duì)是你應(yīng)該感興趣的一種JVM語言。它已經(jīng)成為了一種受到開發(fā)者信任的成熟選擇,這時(shí)Java開發(fā)商的傷害,以及動(dòng)態(tài)類型這些都不是問題。
無論如何,我都不會(huì)是那種爭(zhēng)論何時(shí)給玩轉(zhuǎn)一種編程語言一次機(jī)會(huì)的人。
Java變得過分充實(shí)
Java開發(fā)者可以在Groovy中深入編程并且變得多產(chǎn)。匹配Java的語言有希望或者這看起來將會(huì)是未來的趨勢(shì),2.0發(fā)行版中已經(jīng)加入了Java7項(xiàng)目的Coin增強(qiáng).另外Groovy還使得日常使用Java遇到的坎坷變得平滑。安全的導(dǎo)航(?.)以及 Elvis(?:)都算是很棒的例子。
1 // streetName will be null if user or
2 // user.address is null - no NPE thrown
3 def streetName = user?.address?.street
4 // traditional ternary operator usage
5 def displayName = user.name ? user.name : "Anonymous"
6 // more compact Elvis operator - does same as above
7 def displayName = user.name ?: "Anonymous"
“Groovy是一種多樣性的JVM語言.使用一種同Java相近的語法,這種語言是易學(xué)的,并且允許你編寫從腳本到完整的應(yīng)用程序代碼,包括強(qiáng)大的DSL(領(lǐng)域特定語言)。Groovy很可能是JVM上唯一使得運(yùn)行時(shí)的元編程、編譯時(shí)的元編程、動(dòng)態(tài)類型以及靜態(tài)類型容易處理的語言?!?
CéDRIC CHAMPEAU,
Groovy中的高級(jí)軟件工程師
閉包(Closure)
我們預(yù)期Groovy將止步于句法功能,然而我們卻在文檔中又發(fā)現(xiàn)“閉包”。為什么稱這超越了我們的預(yù)期呢,因?yàn)镚roovy函數(shù)值中的一等公民、更高級(jí)別的函數(shù)以及l(fā)ambda表達(dá)式,這些都得到了支持。
1 square = { it * it } // ‘it’ refers to value passed to the function
2 [ 1, 2, 3, 4 ].collect(square) // [2, 4, 9, 16]
標(biāo)準(zhǔn)庫(kù)對(duì)于閉包恰到好處的應(yīng)用使得使用它們成為一種享受,并且也證明了它們的實(shí)力。下面是使用閉包的語法糖作為方法后面參數(shù)的好例子:
1 def list = ["a","b","c","d"]
2 def newList = []
3 list.collect( newList ) {
4 it.toUpperCase()
5 }
6 println newList // [A, B, C, D]
集合
幾乎所有的應(yīng)用程序都依賴于集合。不幸的是集合大量的戳到了Java的痛處。而如果你懷疑我的這種說法,請(qǐng)嘗試做一些有趣的JSON操作。Groovy為集合的定義將原有的語法打包,并且為了強(qiáng)大的可操作能力,著重使用了閉包。
1 def names = ["Ted", "Fred", "Jed", "Ned"]
2 println names //[Ted, Fred, Jed, Ned]
3 def shortNames = names.findAll { it.size() <= 3 }
4 println shortNames.size() // 3
5 shortNames.each { println it } // Ted
6 // Jed
7 // Ned
靜態(tài)類型
人們常常振奮于動(dòng)態(tài)語言,因?yàn)槟阌煤苌俚拇a就能獲得更多的功能。這常常很少被理解,而剩下的被帶回去維護(hù)。因此我們能夠看到越來越多的動(dòng)態(tài)語言獲得靜態(tài)類型,而且反之亦然。
Groovy2.0加入了靜態(tài)類型
靜態(tài)類型緣何且如何提升了Groovy?
“靜態(tài)檢查使得從Java到Groovy的轉(zhuǎn)型的之路更加的平滑。許多人加入(后續(xù)還有更多人加入)Groovy,因?yàn)樗p量級(jí)的語法,以及所有被移除的樣板,但是,舉個(gè)例子,不想(或者不需要)使用動(dòng)態(tài)特性。他們往往很難理解Groovy編譯時(shí)不像他們以前那樣拋出錯(cuò)誤,因?yàn)樗麄儗?shí)在是不能理解Groovy是一門動(dòng)態(tài)語言。對(duì)于他們來說,我們現(xiàn)在有了@TypeChecked。第二個(gè)原因是性能表現(xiàn),由于Groovy仍然支持更老的JDK(現(xiàn)在是1.5),而動(dòng)態(tài)調(diào)用支持對(duì)它們不可用,因此為了代碼的關(guān)鍵性能部分,你可以有靜態(tài)的編譯了的代碼。還要注意的是,靜態(tài)編譯對(duì)于那些想要免受猴急修補(bǔ)(monkey patching)的框架開發(fā)者來說是有趣的(基于能夠在運(yùn)行時(shí)改變一個(gè)方法的行為這一事實(shí))。”
Cédric Champeau
Groovy的高級(jí)程序工程師
Groovy也不例外,靜態(tài)檢查可以通過相關(guān)代碼中的@Typechecked注解實(shí)現(xiàn).
01 import groovy.transform.TypeChecked
02 void someMethod() {} <&br>
03 @TypeChecked
04 void test() {
05 // compilation error:
06 // cannot find matching method sommeeMethod()
07 sommeeMethod()
08 def name = "Marion"
09 // compilation error:
10 // the variable naaammme is undeclared
11 println naaammme
12 }
你最喜歡的,用Groovy寫成的應(yīng)用程序/框架/庫(kù)是什么?還有為什么你對(duì)它飽含激情?你能說出超過一種么?
“那很簡(jiǎn)單,我最感到熱情的就是Griffon,一個(gè)基于JVM的桌面應(yīng)用程序開發(fā)平臺(tái)。Groovy被用于作為框架中的原生語言,而其他JVM語言可能也被這樣對(duì)待。對(duì)于web開發(fā),沒有Grails我不會(huì)知道我會(huì)怎樣編寫web應(yīng)用,簡(jiǎn)單來說這東西不用提刷新,還有點(diǎn)意思。Gradle是我工具箱中另外一個(gè)寶貝,不管什么時(shí)候一有機(jī)會(huì)我就用它。最后,Spock展示了Groovy編譯器的強(qiáng)大,還有AST表達(dá)式通過一個(gè)簡(jiǎn)單但是十分強(qiáng)大的測(cè)試DSL進(jìn)行處理。”
Andres Almiray,
Groovy 提交貢獻(xiàn)者
Fantom
“Fantom是一個(gè)優(yōu)雅的,強(qiáng)烈關(guān)注并發(fā)和可移植性的新一代語言。不可變性深深融入了Fantom 的類型系統(tǒng),并且并發(fā)使用了演員(actor)模型。Fantom是著意于輕便性來設(shè)計(jì)的,并且對(duì)于Java VM和JavaScript/HTML都有產(chǎn)品質(zhì)量級(jí)別的實(shí)現(xiàn)。Fantom務(wù)實(shí)于關(guān)注靜態(tài)類型的風(fēng)格,但是也能輕易的允許進(jìn)行動(dòng)態(tài)編程。它是一種面向?qū)ο笳Z言,但也包含了第一類函數(shù)(first class functions),并且許多的標(biāo)準(zhǔn)API都應(yīng)用了閉包?!?/p>
Brian Frank,
Fantom創(chuàng)始人
Fantom入門
Fantom同我們?cè)谶@個(gè)報(bào)告中觀察的大部分其他語言有一點(diǎn)點(diǎn)不同之處,它的目標(biāo)是多平臺(tái)?;贘VM、.Net和JavaScript的編譯現(xiàn)在都已經(jīng)提供支持了,并且鑒于他們已經(jīng)就位的基礎(chǔ)設(shè)施,它應(yīng)該也可能瞄準(zhǔn)了其它的目標(biāo)平臺(tái)。
但是盡管可移植性和平臺(tái)成熟度因素是Fantom作者(Brian 和 Andy Frank)考慮的重要問題,它也不是他們要定義這門語言的出處。他們聲稱Fantom是一個(gè)實(shí)用的語言,就是為了干實(shí)事的。
第一步要做的就是設(shè)置環(huán)境還有工具。幸運(yùn)的是,F(xiàn)antom使得這對(duì)于我們來說很容易。Xored搞了一個(gè)稱作F4的基于Eclipse的IDE,它包含了我們需要讓Fantom運(yùn)行起來的一切。
Pod/腳本(Script)
Fantom可以將文件作為腳本執(zhí)行,你只需要在文件中放置一個(gè)帶有main方法的類,并且有一個(gè)可執(zhí)行的扇子(fan)就可以運(yùn)行它了。
1 class HelloWorldishScript
2
3 {
4 static Void main() { echo("Woah! Is it that easy?") }
5 }
然而那不是構(gòu)建Fantom程序的主要方法。對(duì)于大型的項(xiàng)目和提前編譯好模塊的產(chǎn)品級(jí)系統(tǒng),稱作Pod,是使用Fantom的構(gòu)建工具創(chuàng)建的。
構(gòu)建是通過一個(gè)構(gòu)建腳本編排的,它本質(zhì)上是Fantom代碼的另一塊。下面是HTTP服務(wù)器樣本Fantom實(shí)現(xiàn)的構(gòu)建腳本:
01 using build
02 class Build : build::BuildPod
03 {
04 new make()
05 {
06 podName = "FantomHttpProject"
07 summary = ""
08 srcDirs = [./, fan/]
09 depends = ["build 1.0", "sys 1.0", "util 1.0", "concurrent
10
11 1.0"]
12 }
13 }
這里有幾個(gè)事項(xiàng)需要注意,例如依賴規(guī)范允許我們用比jar包更少煩惱的方式,更加容易的構(gòu)建更大型的系統(tǒng)。另外,一個(gè)Pod不單單只定義部署命名空間,還有類型命名空間,統(tǒng)一和簡(jiǎn)化了兩者。現(xiàn)在你可以發(fā)現(xiàn)我們的服務(wù)器依賴的Pod:sys、util和concurrent。
Fantom是如何出現(xiàn)支持多個(gè)后端(.net,JavaScript)的創(chuàng)意的?
“在過去的生涯中,我們使用Java構(gòu)建產(chǎn)品,但是有許多想要將解決方案賣到.NET商店中的問題。因此我們?cè)O(shè)計(jì)了Fantom來同時(shí)針對(duì)兩個(gè)生態(tài)系統(tǒng)。而當(dāng)我們開始開發(fā)現(xiàn)在的產(chǎn)品時(shí),我們將Fantom轉(zhuǎn)入一個(gè)新的方向,目標(biāo)是我們運(yùn)行在JVM上的后端使用一種語言和代碼庫(kù),而我們的前端運(yùn)行在HTML5瀏覽器上面。多年來,這在目前已經(jīng)成為了一個(gè)非常成功的策略。”
Brian Frank,
Fantom創(chuàng)始人
標(biāo)準(zhǔn)庫(kù)/Elegance
Fantom的身份不僅僅只是基于JVM平臺(tái)(或者事實(shí)上是任何其它的平臺(tái))上的一種語言,而它自身更像就是處在JVM之上的一個(gè)平臺(tái)。平臺(tái)提供了API,而Fantom確保了API的精彩和優(yōu)雅。在最基礎(chǔ)的層面上它提供了幾種文法,像下面這樣的:
1 Duration d := 5s
2 Uri uri := http://google.com
3 Map map := [1:"one", 2:"two"]
擁有一個(gè)周期文法( duration literal)是它的一個(gè)微不足道的小細(xì)節(jié),但是當(dāng)你想要設(shè)置一個(gè)延時(shí)操作的時(shí)候,你就能感覺到好像已經(jīng)有人幫你考慮到了這個(gè)場(chǎng)景。
IO的API涉及到一些基礎(chǔ)的類,如Buf、File、In/OutStreams,它們使用起來令人很愉悅。網(wǎng)絡(luò)互通功能也提供了,還有JSON的支持,DOM操作和圖形庫(kù)。重要的東西都為你而存在著。Util這個(gè)pod也包含了一些很有用的東西。代替一個(gè)擁有main方法的類,文件服務(wù)器擴(kuò)展了AbstractMain類,并且能自由的傳遞參數(shù)、設(shè)置日志。另一個(gè)使用起來令人很愉悅的API是Fantom的并發(fā)框架,但我們將只用幾分鐘來談?wù)撘幌滤?/p>
互操作(Interop)
所有構(gòu)建于JVM平臺(tái)之上的語言都提供了一些與原生Java代碼協(xié)同工作的能力。這對(duì)于利用Java的龐大生態(tài)系統(tǒng)起到了關(guān)鍵作用。也就是說,要?jiǎng)?chuàng)建一個(gè)比Java好的語言很容易,而創(chuàng)建一個(gè)比Java提供的相當(dāng)?shù)皿w的互操作更好的語言就難了。那部分歸因于對(duì)集合的處理(它有點(diǎn)了老舊且平淡,而且有時(shí)候使用起來有點(diǎn)兒痛苦)。
Fantom提供了一個(gè)Interop類,它有一個(gè)toFan方法和一個(gè)toJava方法,用于來回轉(zhuǎn)換類型。
1 // socket is java.net.socket
2 InStream in := Interop.toFan(socket.getInputStream)
3 OutStream out := Interop.toFan(socket.getOutputStream)
這里你可以發(fā)現(xiàn)我們有了原生的Java Socket,它自然的為我們提供了Java的Input和OutputStream。
使用Interop將他們轉(zhuǎn)換成與Fantom地位相同,并且稍后就使用它們。
靜態(tài)和動(dòng)態(tài)類型?
另一個(gè)任何語言都要審查的主題是這個(gè)語言是否提供靜態(tài)/動(dòng)態(tài)類型的支持。
Fantom在這一點(diǎn)處在中庸的位置,并且我們很喜歡這一點(diǎn)。屬性域(Field)和方法帶有強(qiáng)靜態(tài)了性的特性。但是對(duì)于本地變量,類型就是被推斷出來的。這導(dǎo)致了一種直觀的混合,方法約束是被拼湊出來的,但是你也并不需要給每一樣事物都賦上類型。
自然的,在Fantom中有兩種方法調(diào)用操作。點(diǎn)(.)調(diào)用需要通過編譯器檢查并且是強(qiáng)類型的,箭頭(->)調(diào)用操作則不是。這就從一個(gè)動(dòng)態(tài)類型的語言中獲得了鴨式類型(duck-typing)還有你想要的任何東西。
不可變性(Immutability)&并發(fā)(Concurrency)
Fantom提供了一個(gè)演員(Actor)框架來處理并發(fā)。消息傳遞和鏈?zhǔn)疆惒秸{(diào)用很容納入代碼中。為了創(chuàng)建一個(gè)演員(它將由某一種ActorPool支持,而后通過一個(gè)線程池獲得),你需要擴(kuò)展一個(gè)Actor類,并且重寫(奇怪的是你必須明明白白的給override關(guān)鍵詞框定類型)receive方法。
請(qǐng)注意避免在線程之間分享狀態(tài),F(xiàn)antom將堅(jiān)持要你只傳遞不可變的消息給actor。不可變性在設(shè)計(jì)時(shí)就構(gòu)建到了語言之中,因此你可以構(gòu)建所有的屬性域都是常量的類。編譯器將驗(yàn)證為actor準(zhǔn)備的消息事實(shí)上是不可變的,否則將拋出一個(gè)異常。
比較酷,而且難于發(fā)現(xiàn)的一點(diǎn)就是,如果你真心需要一個(gè)可變的對(duì)象,你可以把它封裝到一個(gè)Unsafe中(不,這不是那個(gè)不安全的概念,而是Fantom中的Unsafe).
1 while(true) {
2 socket := serverSocket.accept
3 a := ServerActor(actorPool)
4 //wrap a mutable socket to sign that we know what are we doing a.send(Unsafe(socket))
5 }
稍后你可以把原來的對(duì)象再取回來.
1 override Obj? receive(Obj? msg) {
2 // Unsafe is just a wrapper, get the socket
3 log.info("Accepted a socket: $DateTime.now")
4 Socket socket := ((Unsafe) msg).val
5 ...
6 }
"你應(yīng)該考慮吧Unsafe作為最后的殺手锏——因?yàn)樗鼤?huì)侵蝕Fantom中的整個(gè)并發(fā)模型.如果你需要傳遞可變的狀態(tài) b/w Actor——你應(yīng)該使用序列化——它是一個(gè)內(nèi)建的特性: http://fantom.org/ doc/docLang/Actors.html#messages"
Andy Frank,
Fantom創(chuàng)始人
這意味著,適當(dāng)?shù)慕鉀Q方案在這里就會(huì)像這樣:
1 while(true) {
2
3 socket := serverSocket.accept
4
5 a := ServerActor(actorPool, socket)
6
7 a.send(“handleRequest”)
8
9 }
這樣我們就能夠?yàn)榻酉聛頌閷?duì)那個(gè)的IO操作存儲(chǔ)套接字對(duì)象(socket),而我們不需要再去傳遞任何不可變的消息了.順便獲得的好處是,這樣代碼看起來更好了.
函數(shù)(Functions)& 閉包(Closures)
Fantom是一種面向?qū)ο蟮恼Z言,它像許多其他的現(xiàn)代編程語言一樣,將函數(shù)做為了頭等公民。下面的例子展示了如何創(chuàng)建一個(gè)Actor,我們將含蓄指定一個(gè)接收函數(shù)向其發(fā)送幾次消息。
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的語法十分的友好,與其他語言沒太大出入。我們大概已經(jīng)發(fā)現(xiàn),它用":="來進(jìn)行變量的賦值,這對(duì)我來說是一種災(zāi)難(對(duì)這樣的小的語法細(xì)節(jié),我并不十分喜歡)。然而IDE對(duì)此支持得十分友好,每當(dāng)你犯此錯(cuò)誤的時(shí)候,它都會(huì)提醒你。
一些小東東
在我們研究Fantom的整過過程中,一些促使這門語言更加棒的小東東會(huì)令我們驚喜.例如,支持null的類:它們是能夠聲明一個(gè)接受或者不接受null作為參數(shù)的方法的一種類型.
1 Str // never stores null
2 Str? // might store null
通過這種方式,代碼不會(huì)受到空(null)檢查的污染,并且同Java的互操作變得更加的簡(jiǎn)單了.
值得一提的還有另外一種特性.那就是帶有變量插值(Variable interpolation)的多行字符串(Multi-line String):
1 header :=
2 "HTTP/1.1 $returnCode $status
3 Server: Fantom HTTP Server 1.0
4 Date: ${DateTime.now}
5 Content-type: ${contentType}
6 Content-length: ${content.size}
7 ".toBuf
參數(shù)可以有默認(rèn)值,支持混合和聲明式編程,還有運(yùn)算符重載.每一樣?xùn)|西都各得其所.
只有一件東西我們沒有看到并感到是一種缺陷,那就是元組(tuples).然而我們只在想要多返回(mutilple return)時(shí)才需要那個(gè)東西,因此使用一個(gè)列表(list)就足夠了.
Clojure
“我著手創(chuàng)建一種語言,意在應(yīng)對(duì)我在使用Java和C#編寫的一些類型的應(yīng)用程序——像廣播自動(dòng)化、調(diào)度以及選舉系統(tǒng)之類那些東西——它們?cè)S多都需要解決的并發(fā)問題.我發(fā)現(xiàn)只用面向?qū)ο缶幊毯陀媚切┱Z言的并發(fā)方法,對(duì)于處理這些類型的問題并不怎么夠好——它們太難了。我是List的擁護(hù)者,還有其它的函數(shù)式語言,而我想要做的就是解決那些問題,創(chuàng)造一種立足于實(shí)際的語言,再也不用拿Java來編程了.”
Rich Hickey,
Clojure創(chuàng)始人在2009年InfoQ訪談中
Clojure 入門
Clojure 第一次出現(xiàn)在2007年 。和一些成熟的語言相比,它相對(duì)是新的。Rich Hickey創(chuàng)造了它。它作為L(zhǎng)isp方言在JVM上面。版本1.0出現(xiàn)在2009年。它的名字是一個(gè)雙關(guān)語在C(C#), L (Lisp) and J (Java)。當(dāng)前Clojure的1.4。Clojure是開源的(發(fā)布在Eclipse 公共許可證 v1.0—EPL)
當(dāng)你開始閱讀Clojure的文檔的之后,我們決定開始配置環(huán)境變量。我們使用Leiningen(它是一個(gè)為Clojure準(zhǔn)備的構(gòu)建工具) 。對(duì)Clojure來說,Leiningen (或者縮寫Lein) 能夠執(zhí)行大部分任務(wù)。它能夠執(zhí)行我們期待的來自Maven相關(guān)的例如:
創(chuàng)造一個(gè)項(xiàng)目骨架 (想一想: Maven 原型)
操作依賴
編譯 Clojure代碼到JVM classes類
運(yùn)行測(cè)試
發(fā)布構(gòu)件到一個(gè)中央倉(cāng)庫(kù)
假如你使用過Maven, 你會(huì)感覺使用Lein非常的舒適。事實(shí)上,Lein 甚至支持Maven的依賴。然而兩者的主要不同點(diǎn)在于 Lein的項(xiàng)目文件由 Clojure寫成。然而Maven使用XML(pom.xml)。盡管有可能去開發(fā)用Clojure沒有其它的,我們不得不承認(rèn)Lein是一個(gè)非常受歡迎的增加物。它真得是某些事情變得更加簡(jiǎn)單。
開始得到項(xiàng)目骨架,你能夠做像下面這樣:
$ lein new clojure-http-server
集成開發(fā)環(huán)境(IDE)的支持
準(zhǔn)備好基本的項(xiàng)目結(jié)構(gòu)以后,就可以開始編輯代碼了.如果你是Eclipse的長(zhǎng)期使用者,你首先要做的一件事情就是找到一個(gè)可以處理Clojure 的Eclipse插件.它應(yīng)該在一個(gè)成熟的工作空間中提供語法高亮和代碼補(bǔ)全功能.幸運(yùn)的是Eclipse中有了一個(gè)叫做CounterClockWise的Clojure插件.只要安裝了這個(gè)插件,就可以在Eclipse中新建Clojure項(xiàng)目了.
這樣很不錯(cuò),我們不需要再去管將我們已經(jīng)使用過的,前面章節(jié)提到的Lein,在命令行中創(chuàng)建的Clojure項(xiàng)目然后導(dǎo)入到Eclipse中這種麻煩事了。我們預(yù)計(jì)CounterClockWise插件提供了像Eclipse的Maven插件有的在pom.xml和EclipseGUI之間兩種交互方式,所提供的功能.
僅僅為了好玩,我們也看了看Clooj,它是用Clojure本身開發(fā)的一個(gè)輕量級(jí)的Clojure IDE.下載和運(yùn)行它都很容易,不過我們發(fā)現(xiàn)它同Eclipse相比,就有點(diǎn)黯然失色了.
最后我們用了用最難的方式開發(fā)Clojure程序——只在命令行中使用Lein,還有可靠的GVIM作為文本編輯器——主要是想看看Lein是如何詳細(xì)工作的.
交互式解釋器(REPL)
像眾多的函數(shù)式語言, Clojure提供的命令行shell可以直接執(zhí)行Clojure語句。這個(gè)shell對(duì)開發(fā)者非常方便,因?yàn)樗陂_發(fā)中不僅允許你測(cè)試一小端代碼,而且允許你運(yùn)行程序的一部分。
這或許對(duì)用Python,Perl開發(fā)的碼農(nóng)沒什么新鮮的,但對(duì)Java開發(fā)者來說,這無疑帶來更新鮮、更交互的方式來寫代碼。
jvm languages code example
函數(shù)式編程——另外一種思考方式
Clojure是一種非常類似于Lisp和Scheme的函數(shù)式編程語言.函數(shù)式范式同那些習(xí)慣于Java的面向?qū)ο蠓绞讲⑶伊?xí)慣于其副作用的方式非常不同.
函數(shù)式編程推崇:
很少或者完全沒有副作用
如果使用相同的參數(shù)區(qū)調(diào)用,函數(shù)就永遠(yuǎn)返回同一個(gè)結(jié)果(而不是依賴于對(duì)象狀態(tài)的方法)
沒有全局變量
函數(shù)是第一位的對(duì)象
表達(dá)式懶計(jì)算(Lazy evaluation of expression)
這些特性并不是Clojure獨(dú)有的,而是總體上對(duì)函數(shù)式編程都要求的:
1 (defn send-html-response
2 "Html response"
3 [client-socket status title body]
4 (let [html (str "
Clojure提供了優(yōu)秀的同Java庫(kù)的互操作功能.事實(shí)上,對(duì)于一些基本的類,Clojure并沒有提供它自己的抽象,而是超乎你預(yù)期的直接使用了Java類來代替.在這個(gè)HTTP服務(wù)器的示例中,我們從Java中獲取了像Reader和Writer這樣的類:
1 (ns clojure-http-server.core
2 (:require [clojure.string])
3 (:import (java.net ServerSocket SocketException)
4 (java.util Date)
5 (java.io PrintWriter BufferedReader InputStreamReader BufferedOutputStream)))
創(chuàng)建和調(diào)用Java對(duì)象是非常直截了當(dāng)?shù)?而實(shí)際上有兩種形式(在這個(gè)優(yōu)秀的Clojure介紹中描述到了):
1 (def calendar (new GregorianCalendar 2008 Calendar/APRIL 16)) ;
2 April 16, 2008
3 (def calendar (GregorianCalendar. 2008 Calendar/APRIL 16))
4 Calling methods:
5 (. calendar add Calendar/MONTH 2)
6 (. calendar get Calendar/MONTH) ; -> 5
7 (.add calendar Calendar/MONTH 2)
8 (.get calendar Calendar/MONTH) ; -> 7
下面是一個(gè)實(shí)際的樣例:
1 (defn get-reader
2 "Create a Java reader from the input stream of the client socket"
3 [client-socket]
4 (new BufferedReader (new InputStreamReader (.getInputStream client-
5
6 socket))))
然而,對(duì)于一些結(jié)構(gòu),我們決定要使用到Clojure的方式.原生的Java代碼使用StringTokenizer,這樣做違背了不可變對(duì)象的純函數(shù)式原則,還有無副作用的原則.調(diào)用nextToken()方法不僅有副作用(因?yàn)樗薷牧薚onkenize對(duì)象)并且使用同一個(gè)(或許是不存在的)參數(shù)也會(huì)有不同的返回結(jié)果.
由于這個(gè)原因,我們使用Clojure的更加"函數(shù)式"的Split函數(shù):
01 (defn process-request
02 "Parse the HTTP request and decide what to do"
03 [client-socket]
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")
10 [...]
并發(fā)(Concurrency)
Clojure從一開始設(shè)計(jì)對(duì)并發(fā)很上心,而不是事后諸葛亮.使用Clojure編寫多線程應(yīng)用程序非常簡(jiǎn)單,因?yàn)樗械暮瘮?shù)默認(rèn)都實(shí)現(xiàn)了來自Java的Runnable和Callable接口,自身得以允許其任何方法在一個(gè)不同的線程中運(yùn)行。
Clojure也提供了其它特別為并發(fā)而準(zhǔn)備的結(jié)構(gòu),比如原子(atom)和代理(agent),但是我們?cè)谶@個(gè)HTTP服務(wù)器示例中并沒有使用它們,而是選擇熟悉的Java的Thread.
1 (defn new-worker
2 "Spawn a new thread"
3 [client-socket]
4 (.start (new Thread (fn [] (respond-to-client client-socket)))))
有關(guān)方法使用順序的問題
我們認(rèn)識(shí)到的一件事情是在源代碼文件中,方法的順序是嚴(yán)格的。函數(shù)必須在它們第一次被使用之前被定義。另外一種選擇是,你可以在一個(gè)函數(shù)被實(shí)際定義之前利用一種特殊的聲明格式,來使用函數(shù)。這讓我們想起了C/C++的運(yùn)作方式,它們使用頭文件和函數(shù)聲明。
Ceylon
"Ceylon由Java啟發(fā)產(chǎn)生。我們?cè)噲D去創(chuàng)造一個(gè)更強(qiáng)大的語言。我們時(shí)刻牢記不能做的更加糟糕比起Java - 那就是說,我們不想打破某些Java做的好的地方或者我們不想失去某些特征。那就是在團(tuán)隊(duì)環(huán)境中,Java能夠編寫大的穩(wěn)定的項(xiàng)目。"
Gavin King,
Ceylon 的語言的創(chuàng)造者
Ceylon 入門
作為一個(gè)Java開發(fā)者, 你將會(huì)希望能夠非??斓夭捎肅eylon。在項(xiàng)目的主頁(yè)上,開始編碼基本上相當(dāng)于走Ceylon新的旅程 - 從那里你將會(huì)得到大部分關(guān)于語言的信息。
對(duì)于Ceylon,我們首先要關(guān)注的是它精心準(zhǔn)備的“基礎(chǔ)設(shè)施(infrastructural)”部分。在這里基礎(chǔ)設(shè)施的意思是模塊。Ceylon運(yùn)行時(shí)是基于JBoss的模塊(Module)的,并且Ceylon用模塊代表一切事物。即使是你新打造的項(xiàng)目實(shí)際上也是一個(gè)Ceylon模塊,它是通過項(xiàng)目文件夾里面的一個(gè)module.ceylon文件來聲明的:
1 module com.zt "1.0.0" {
2 import ceylon.interop.java "0.4.1";
3 import java.base "7";
4 }
這里的意思是這個(gè)模塊叫做com.zt,它的版本號(hào)是1.0.0。關(guān)于模塊聲明的格式需要注意:模塊名不需要在引號(hào)中,而模塊的版本號(hào)需要在引號(hào)中,比如"1.0.0"。這有點(diǎn)怪吧,因?yàn)榘姹咎?hào)是一個(gè)數(shù)字而包不是的。為什么不省掉版本號(hào)的引號(hào)呢?可能是因?yàn)榘姹咎?hào)會(huì)包含非數(shù)字的字符吧,像"1.0.0-SNAPSHOT’。但是完全省掉引號(hào),Ceylon也可能理解。
Ceylon出色的踐行了模塊化,并且將運(yùn)行時(shí)構(gòu)建于Jboss模組。這些為什么如此重要?
首先,沒有模塊化,你就不能在不去破壞一些東西的前提下解決問題。
其次,沒有模塊化,你的程序就不能跨平臺(tái)運(yùn)行。Java龐大的SDK意味著我不能確定在一個(gè)JavaScript上面能跑Java程序。Ceylon"小巧"的語言模塊能在一個(gè)web瀏覽器上面工作得像在服務(wù)器上面一樣好。
第三,沒有模塊化,你就不能構(gòu)建在模塊產(chǎn)品上作業(yè)的工具,也不能用模塊資源庫(kù)代替放置在你硬盤的單個(gè)的文件。
第四,沒有語言級(jí)別的模塊化,你就會(huì)滋長(zhǎng)像Maven和OSGI一樣的可怕的過度工程的技術(shù)。你也不得不在臃腫龐大的像JavaSE和JavaEE(JavaEE6之前)這樣的平臺(tái)上工作。
Gavin King
Ceylon創(chuàng)始人
其次,run.ceylon是項(xiàng)目的主文件,而我們可以實(shí)現(xiàn)run()方法來啟動(dòng)我們的應(yīng)用:
1 void run(){
2 print("Hello from Ceylon!");
3 }
Ceylon中的方法(或者函數(shù))可以是多帶帶的,而不屬于任何一個(gè)類。屬性(attribute)也同樣適用這一特性。實(shí)際上,發(fā)現(xiàn)它編譯成了什么是相當(dāng)有趣的一件事。每一個(gè)多帶帶的屬性(或域)和方法都被編譯成了一個(gè)專用的類。那意味著下面的這段代碼被編譯成了兩個(gè)類,port_.class和run_.class——它們也同樣包含了公共靜態(tài)返回值為空的main (public static void main)方法.
01 void run(){
02 ServerSocket server = ServerSocket(port);
03 print("Listening for connections on port " port "...");
04 while(true){
05 Socket socket = server.accept();
06 print("New client connection accepted!");
07 HTTPServer httpServer = HTTPServer(socket);
08
09 Thread threadRunner = Thread(httpServer);
10 threadRunner.start();
11 }
12 }
在這個(gè)過程中我們了解到,Ceylon工具編譯和打包了所有東西到Ceylon ARchive(.car)中,并且在那之后對(duì)自身做了清理,所以你不會(huì)在編譯之后找到class文件——我們只有解壓這個(gè)壓縮了的存檔才能訪問class文件。
工具的一個(gè)很棒的部分是,當(dāng)一個(gè)新的依賴被加入到module.ceylon文件中時(shí),依賴會(huì)自動(dòng)從CeylonHerd網(wǎng)站下載,并且由IDE處理識(shí)別。那是這個(gè)語言的基礎(chǔ)設(shè)施的另外一個(gè)長(zhǎng)處。
同Java的互操作性
同Java的互操作性對(duì)我來說是一個(gè)棘手的部分。首先,你必須將依賴添加到module.ceylon中,使得程序能夠使用Java類:
1 import java.base "7";
當(dāng)你在Ceylon中使用Java類時(shí),你可能會(huì)直觀的期望著可以像在Java程序中一樣正常的使用Java類的實(shí)例中的所有方法。但是在Ceylon中不是這樣的。下面是一個(gè)實(shí)例:
1 value printWriter = PrintWriter(socket.outputStream);
你希望能在socket上面使用getOutputStream()方法,但是Ceylon將它解釋成了一個(gè)域的獲取器(getter),并且用一個(gè)屬性訪問修飾(大概是?)來取代它的語義。盡管當(dāng)你檢查它的來源時(shí),getOutputStream()并不是一個(gè)獲取器。
jvm languages screenshot
類型轉(zhuǎn)換是我們需要面對(duì)的第二個(gè)挑戰(zhàn)。Ceylon類型和Java類型之間有一種特殊的映射。
因此fileData一定是一個(gè)字節(jié)數(shù)組類型,而根據(jù)類型映射規(guī)則,它應(yīng)該在Ceylon中被聲明為一個(gè)Array:
1 Array fileData = arrayOfSize {
2 size = fileLength; element = 0;
3 };
噢,我的...(天),不好了:
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)
顯然,為了讓類型轉(zhuǎn)換能正常的工作,我們不得不 (在module.ceylon中)導(dǎo)入特殊的Java互操作模塊:
1 import ceylon.interop.java "0.4.1"
然后才有可能使用由互操作模塊提供的幫助方法。下面就是類型轉(zhuǎn)換所需要的代碼:
1 Array
2 value fileIn = FileInputStream(file);
3 fileIn.read(fileData);
"事實(shí)上這是一個(gè)真正令人痛苦的角落。我們處理Java原生類型的一般性策略在處理器容器類型時(shí)是不健全的,這通常是很好的,因?yàn)镴ava的泛型并不是基于Java原生類型的抽象:你不能擁有一個(gè)List。但是當(dāng)Java有了數(shù)組(array)時(shí),它們是特殊類型的容器類型,是基于原生類型的抽象,我們因此需要用特殊的方法處理這種特殊的情況。這個(gè)問題將會(huì)在未來版本的編譯器中被修復(fù)。"
Gavin King
Ceylon創(chuàng)始人
基本上,畢竟HTTP服務(wù)器實(shí)現(xiàn)的所有問題都已經(jīng)在運(yùn)行著了,但是在運(yùn)行時(shí)會(huì)出現(xiàn)驚喜:null檢查。
現(xiàn)在應(yīng)用程序啟動(dòng)了,但是在客戶端提交了一個(gè)GET請(qǐng)求以后,它失敗了,打印如下的調(diào)用棧:
1 Exception in thread "Thread-1" java.lang.NullPointerException
2
3 at com.redhat.ceylon.compiler.java.Util.checkNull(Util.java:478)
4 at com.zt.HTTPServer.run(classes.ceylon:19)
這個(gè)令人驚奇的部分位于classes.ceylon第19行,這里有這樣一行代碼:
1 String input = bufferedReader.readLine();
這里是一些如何使用javap幫助來閱讀反編譯代碼的知識(shí)。字節(jié)碼透露了Ceylon編譯器在Java類的方法返回之后插入了null檢查。因此要解決這個(gè)問題,我們不得不在聲明中使用?后綴:
1 String? input = bufferedReader.readLine();
但是隨后就會(huì)有這個(gè)值是如何被使用的問題。例如,如果它是作為StringTokenizer的一個(gè)參數(shù)使用,那么初始化這個(gè)類應(yīng)該也會(huì)失敗。因此就有了下面這種蹩腳的解決辦法:
1 if(!exists input){
2 return;
3 }
肯定應(yīng)該有處理這種情況的更好方式,而不是像眼前這樣就處理了問題。
"如果有你確實(shí)不想在null的情況下再做一些什么事情的場(chǎng)景,你可以就寫“assert(exists input);",那就會(huì)將范圍縮小到非空的類型。”
Gavin King
Kotlin
“我們認(rèn)為Kotlin的定位是一種現(xiàn)代化工業(yè)語言:它專注于代碼重用和可讀性的彈性抽象,以及面向早期錯(cuò)誤偵測(cè),和明確捕獲維護(hù)與清理的意圖,這些問題的靜態(tài)類型安全性。Kotlin最重要的使用場(chǎng)景之一是對(duì)于一個(gè)龐大的Java代碼庫(kù),其開發(fā)者需要一個(gè)更棒的語言:你能夠?qū)ava和Kotlin自由混合,遷移可以是漸進(jìn)式的,不需要一下子對(duì)整個(gè)代碼庫(kù)進(jìn)行改變?!?/p>
Andrey Breslav
Kotlin創(chuàng)始人
考察Kotin時(shí),作為JetBrains創(chuàng)造的產(chǎn)品,我們首先要面對(duì)的問題就是其他IDE對(duì)它的支持。由于我們大多數(shù)人都是Eclipse的用戶,切換到IntelliJ IDEA環(huán)境總是困難的,但是如果你想使用Kotlin環(huán)境進(jìn)行編碼工作,這總是免不了的一個(gè)步驟。安裝Kotlin插件式相當(dāng)容易的,Kotlin的artifact也是,但令人難堪的就是支持沒有推廣到其它的IDE。也許我們應(yīng)該通過某種恰當(dāng)?shù)姆绞较騄etBrains團(tuán)隊(duì)就這個(gè)問題發(fā)出一些暗示才行?:)
優(yōu)雅的編碼
使用Kotin進(jìn)行編碼確實(shí)會(huì)生產(chǎn)出一些非常優(yōu)雅的代碼。它消除的進(jìn)行null檢查的必要,使用主構(gòu)造器(primary constructors),智能的轉(zhuǎn)換(smart case),范圍表達(dá)式...這個(gè)清單還沒完呢。那就讓我們先來看個(gè)例子吧。
以我們Java的背景來看,我們不喜歡使用when結(jié)構(gòu)鑄造(casting)的is組合。在Java中,將它們看作是鑄造的(A)對(duì)象的實(shí)例,各自進(jìn)行轉(zhuǎn)換。is的使用也將會(huì)引用一個(gè)鑄造(cast),如果你率先直接使用這個(gè)對(duì)象的話。比如:if(stream is Reader)stream.close()。在這個(gè)例子中,close方法在Reader接口上被調(diào)用了。這應(yīng)該也可以像這樣子表述:if(stream is Reader)(stream as Reader).close(),但是不需要額外的代碼了。這種使用when的組合允許你對(duì)變量進(jìn)行切換,但當(dāng)你能夠獲得一種更加豐富的參與時(shí),就不僅僅只使用它的值了。你可以考慮像下面這樣:
1 when (stream) {
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)
8 }
如果你能想象你將會(huì)怎樣在Java中實(shí)現(xiàn)這個(gè)示例,你就會(huì)覺得上面的代碼有多干凈優(yōu)雅了。有趣的是C#也有類似的is和as的使用方式,同樣也實(shí)現(xiàn)了可為空(nullable)類型。
利用參數(shù)命名(parameter naming)的方法調(diào)用也是默認(rèn)的。這種問題也能在你需要調(diào)用一個(gè)擁有五個(gè)布爾值參數(shù)的方法,確保你傳入的true和false順序正確時(shí),讓你感到頭痛。參數(shù)命名通過在方法調(diào)用上帶上參數(shù)名稱來繞過參數(shù)的混淆。這樣很棒。這在過去已經(jīng)被其他的語言和腳本框架再一次做到了,它們?cè)试S當(dāng)用戶樂于接受一些默認(rèn)參數(shù)值時(shí),方法調(diào)用可以省略掉某些參數(shù)。讓我們來看一個(gè)例子:
1 private fun print(out : PrintWriter,
2 pre : String,
3 contentType: String = “text/html”,
4 contentLength : Long = -1.toLong(),
5 title : String, body : () -> Unit)
這里我們有了一個(gè)帶有若干個(gè)String參數(shù)的方法,還有一個(gè)作為輸入的函數(shù)。它使用下面的方式來調(diào)用。請(qǐng)注意contentType和contentLength參數(shù)都被省略了,意味著聲明中的默認(rèn)值將被使用。
1 print(out = out,
2 pre = "HTTP/1.0 404 Not Found",
3 title = "File Not Found",
4 body = {out.println("
1 404 File Not Found: " +
2 file.getPath() + "
3 ")})
那么這有什么意義呢?這將省掉很多方法重載!雖然言重了一點(diǎn),但是一想到Java在過去超過20多年時(shí)間里沒有像這樣的好東西出世,就足以讓這個(gè)功能令人驚奇了。有時(shí)候就有一點(diǎn)點(diǎn)感覺你是在暗無天日的編寫著代碼。加點(diǎn)油吧,Java,你得奮起直追才行!
Kotlin將幫助你編寫安全的代碼,除非你不想這樣做
首先,你可以對(duì)NPE(空指針異常)說再見了!Kotlin使用“可為空(nullable
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65843.html
摘要:虛擬機(jī)包括一套字節(jié)碼指令集一組寄存器一個(gè)棧一個(gè)垃圾回收堆和一個(gè)存儲(chǔ)方法域。而使用虛擬機(jī)是實(shí)現(xiàn)這一特點(diǎn)的關(guān)鍵。虛擬機(jī)在執(zhí)行字節(jié)碼時(shí),把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行。此內(nèi)存區(qū)域是唯一一個(gè)在虛擬機(jī)規(guī)范中沒有規(guī)定任何情況的區(qū)域。 1、 什么是JVM? JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來的計(jì)算機(jī),...
摘要:由硬件和軟件所組成,沒有安裝任何軟件的計(jì)算機(jī)稱為裸機(jī)。計(jì)算機(jī)的硬件是指計(jì)算機(jī)系統(tǒng)中由電子,機(jī)械和光電元件等組成的各種物理裝置的總稱。計(jì)算機(jī)軟件數(shù)據(jù)和指令的集合,分為系統(tǒng)軟件和應(yīng)用軟件。提供了程序運(yùn)行的環(huán)境核心類庫(kù)。 一、計(jì)算機(jī)基礎(chǔ) 1. 計(jì)算機(jī) 全稱為電子計(jì)算機(jī),俗稱電腦。 是一種能夠按照程序運(yùn)行,自動(dòng)且高速地處理海量數(shù)據(jù)的現(xiàn)代化智能電子設(shè)備。 由硬件和軟件所組成,沒有安裝任何軟件的...
摘要:外部存儲(chǔ)器可用于長(zhǎng)期保存大量程序和數(shù)據(jù),其成本低容量大,但速度較慢。 1_計(jì)算機(jī)概述(了解) A:什么是計(jì)算機(jī)?計(jì)算機(jī)在生活中的應(yīng)用舉例 計(jì)算機(jī)(Computer)全稱:電子計(jì)算機(jī),俗稱電腦。是一種能夠按照程序運(yùn)行,自動(dòng)、高速處理海量數(shù)據(jù)的現(xiàn)代化智能電子設(shè)備。由硬件和軟件所組成,沒有安裝任何軟件的計(jì)算機(jī)稱為裸機(jī)。常見的形式有臺(tái)式計(jì)算機(jī)、筆記本計(jì)算機(jī)、大型計(jì)算機(jī)等。 應(yīng)用舉例 ...
摘要:對(duì)字節(jié)碼文件進(jìn)行解釋執(zhí)行,把字節(jié)碼翻譯成相關(guān)平臺(tái)上的機(jī)器指令。使用命令可對(duì)字節(jié)碼文件以及配置文件進(jìn)行打包可對(duì)一個(gè)由多個(gè)字節(jié)碼文件和配置文件等資源文件構(gòu)成的項(xiàng)目進(jìn)行打包。和不存在永久代這種說法。 Java技術(shù)體系 從廣義上講,Clojure、JRuby、Groovy等運(yùn)行于Java虛擬機(jī)上的語言及其相關(guān)的程序都屬于Java技術(shù)體系中的一員。如果僅從傳統(tǒng)意義上來看,Sun官方所定義的Jav...
摘要:在本文,筆者將與大家概覽的體系結(jié)構(gòu)與工作方式。將第條和第條指令分別是將兩個(gè)局部變量入棧,然后相加。最后一條指令是,這條指令執(zhí)行完后當(dāng)前的這個(gè)方法對(duì)應(yīng)的這些部件會(huì)被回收,局部變量區(qū)的所有值將全部釋放,寄存器會(huì)被銷魂,在棧中與這個(gè)方 Java之所以號(hào)稱一次編譯,到處運(yùn)行,主要原因是JVM屏蔽了各個(gè)計(jì)算機(jī)平臺(tái)相關(guān)的軟件(大多指系統(tǒng))或者硬件之間的差異,使得與平臺(tái)相關(guān)的耦合統(tǒng)一由JVM提供者來...
閱讀 3603·2021-11-24 10:25
閱讀 2508·2021-11-24 09:38
閱讀 1217·2021-09-08 10:41
閱讀 2903·2021-09-01 10:42
閱讀 2569·2021-07-25 21:37
閱讀 1981·2019-08-30 15:56
閱讀 914·2019-08-30 15:55
閱讀 2749·2019-08-30 15:54