原文:
http://in.relation.to/Bloggers/IntroductionToCeylonPart1這是Ceylon語言系列文章的第一部分。要注意的是語言的特性可能在最終版發布之前發生改變。
關于Ceylon
Ceylon是一門新的語言,它運行在Java虛擬機上,目前正有我所在的小組開發,它隸屬于RedHat。我們都是Java和Java生態系統的粉絲,因為它的實用性、廣闊的文化氛圍和開發社區、天生適用于商業應用以及可移植性。然而我們必須承認這門語言和其現有的類庫,已經過了15年的發展,它不能再提供更好的功能來解決現在的商業問題。
- Ceylon的設計目標包括:
- Java和C#開發者可以很容易的學習和掌握。
- 消除了一下Java的啰嗦語法,使其容易閱讀。
- 更加類型安全。
- 提供一個聲明式的語法來表達層級信息,比如定義用戶接口、結構化數據以及系統配置,這導致了Java平臺過度的依賴于XML。
- 支持不變對象和高級函數(功能)
- 極好的元數據編程支持,這使得編寫框架變得非常容易
- 提供內置模塊解決方案
最重要的是,Ceylon的設計目的是讓大型團隊更好的協作
Ceylon編譯器還沒有完成,所以你還不能使用Ceylon編寫代碼。不管怎樣,我愿意讓社區參與語言和SDK的開發,所以這個系列文章是給對Ceylon感興趣的人一個提前預覽。
讓我們從頭開始吧。
編寫一個簡單的程序
這是一個經典的例子程序。
void hello() {
writeLine("Hello, World!");
}
這個方法在控制臺打印出“Hello, World!”。這是一個頂層方法就像C語言函數--它直屬于包含它的包中,它不是一個任何類型的成員。你不需要接收一個對象來調用頂層方法,你只要像下面這樣就可以調用它:
hello();
Ceylon沒有Java風格的靜態方法,但是你可以使用頂層方法來充當同樣的角色。靜態方法存在的問題是它打亂了程序的塊結構。Ceylon擁有嚴格的塊結構--一個內嵌的塊總是可以在所有包含它的塊中被訪問。這與Java的靜態方法不同。
這個方法使用了void關鍵字,這表示方法沒有返回值。當方法被執行時,它調用另一個頂層方法writeLine(),這個方法在控制臺中顯示它的參數。
連同void關鍵字,還有一個命名為Void的類型,其再任何void方法中被返回,這也是Ceylon類型系統的根類型。
doc "The root type, supertype of
both Object (definite values)
and Nothing (the null value)."
see (Nothing, Object)
shared abstract class Void() {}
可能對void方法有一個返回類型很不解。對此的解釋就是Ceylon內所有的方法都是函數。(但未必都是函數--比如hello(),一個Ceylon函數能夠有邊緣效應。)
在一個非常抽象層里,每個方法都接受參數并有返回結果。對于Void類型,簡單來說就是提供表示未知值和未知類型的一種途徑。你可以給Void分配任何值,包括null,但是它無法被再次回收,即使知道它的類型值是什么。因此理論上void方法有返回值,只是我們無法知道關于它值的任何信息。現在這聽起來可能沒什么用處,但是當我們討論first-class函數和Ceylon類型安全的元數據模型時將證明其非常有用。
添加內嵌的文檔
通常給像hello()這樣重要的方法上添加注釋文檔是一個好習慣。一個方式就是使用C語言風格的注釋,就像下面這樣:
/* The classic Hello World program */
void hello() {
writeLine("Hello, World!");
}
或者像這樣:
//The classic Hello World program
void hello() {
writeLine("Hello, World!");
}
但是更好的方式是使用doc注解來寫注釋。
doc "The classic Hello World program"
void hello() {
writeLine("Hello, World!");
}
doc注解包含的文檔被包含在Ceylon文檔編譯器輸出中。文檔編譯器還將支持其它幾個注解,包括by,用來指定程序作者;see,用來關聯其它代碼元素;以及throws,報告用戶程序執行拋出的異常類型。
doc "The classic Hello World program"
by "Gavin"
see (goodbye)
throws (IOException)
void hello() {
writeLine("Hello, World!");
}
同樣還有一個deprecated注解,用來說明程序元素在將來的版本中會被移除。
注意,當一個注解的參數是字面原文形式,那么久不需要有關閉括號。
像doc,by,see和deprecated這類的注解不是一個關鍵字。它們只是普通的標識符。同樣的對于語言中定義的注解:abstract,variable,shared,formal,actual和friends也都不是關鍵字。但void是關鍵字。
字符串和字符串內插
Hello World程序--現在廣泛流行--提供非常有限的用戶體驗。一個更典型的例子在不同的運行時產生不同的輸出。
讓我們來詢問我們的程序,以便它告訴我們更多關于它自己的信息。
doc "The Hello World program
version 1.1!"
void hello() {
writeLine("Hello, this is Ceylon " process.languageVersion
" running on Java " process.javaVersion "!");
} 我們能看到Ceylon的字符串提供的兩個好處。第一就是可以分割字符串為多行,這對于在doc注解中寫文檔非常有幫助。第二個就是我們可以在字符串內部插入表達式,從技術上來講,一個帶有表達式的字符串不在是一個真正的字符串了,而被看做一個字符串模板。
一個字符串表達式必須開頭和結尾都必須是字符串,下面的語法是錯誤的:
writeLine("Hello, this is Ceylon " process.languageVersion); //compile error!
在最后加上一個空的字符串就能修正上面例子的錯誤。
writeLine("Hello, this is Ceylon " process.languageVersion "");
注意,在Ceylon中這不是唯一連接字符串的方法。其實這只是對于在不變的字符串中插入變量或表達式有用。+操作符可作為另外一種選擇,還有更多靈活的例子:
writeLine("Hello, this is Ceylon " + process.languageVersion +
" running on Java " + process.javaVersion + "!");
但是不要看到這兩種方式的輸出是一樣的就認為它們是等價的。+操作符僅僅是對表達式求值并產生一個不可變的String對象。而String模板是一個Gettable<String>的表達式,其不馬上對內插表達式求值(有點像Hibernate里的懶加載)。如果你打印String模板對象兩次,你可能會看到兩個不同的輸出。其實,String模板是一種closure(閉包)--一個重要的概念,我將在后面介紹。
處理沒找到的對象
大多數程序都要求接收輸入并產生輸出,并且程序依賴于接收的輸入。當然,這么做對用戶來說有點苛刻,但是,一些額外的工作必須做!
對此,提供一個改良版本的Hello World程序,其從命令行接收一個名字作為輸入。我們必須考慮在命令行什么都沒輸入的情況,這樣就能給我們一個機會去體驗Ceylon如何處理Null值,這可與Java于C#的處理方式有著很大的不同。
doc "Print a personalized greeting"
void hello() {
String? name = process.arguments.first;
String greeting;
if (exists name) {
greeting = "Hello, " name "!";
}
else {
greeting = "Hello, World!";
}
writeLine(greeting);
}
process對象有一個arguments屬性,它持有命令行參數的Sequence(順序)。本地變量name被這些參數初始化,參數如果存在的話,本地變量被聲明為String類型,否則他可能包含一個null值。if(exists...)控制結構用來初始化本地非空變量greeting,如果name不是null的話就內插到消息字符串中。最終,消息被輸出到控制臺。
這與Java不一樣,本地變量,參數和屬性都可以包含null值,但必須明確聲明其類型。這與其它語言持有類型安全的null值不同,在Ceylon中可選的類型不是一個封裝定值的algebraic數據類型,而是一個ad-hoc union type(聯合類型)。語法T|S表示聯合T和S。一個可選的類型Noting|X代表任何類型,X是一個定值類型。Ceylon允許我們使用縮寫X?代表Noting|X。
doc "The type of null."
shared abstract class Nothing()
of nothing
extends Void() {}
null值關聯一個Nothing類型的實例,但是它不是一個Object的實例。因此這是一種簡單分配本地變量為Null(不是一個可選類型)的方式。
doc "Represents a null reference."
object nothing extends Nothing() {}
doc "Hides the concrete type of nothing."
shared Nothing null = nothing;
Ceylon編譯器也不允許你最T類型的值做任何“危險”的事情,在Java里這樣會拋出NullPointerException。if(exists...)結構讓我們從X?類型提取X類型的值,從而允許我們調用X值的方法。
事實上,沒有可能在一個可選類型的表達式里使用==操作符,你不能像在Java中那樣使用if(x==null)。這有助于避免像Java中的不良操作==,x==y在Java中如果x和y都是null將返回true.
在if(exists...)條件中可以聲明本地變量name:
String greeting;
if (exists String name = process.arguments.first) {
greeting = "Hello, " name "!";
}
else {
greeting = "Hello, World!";
}
writeLine(greeting);
自從我們不能再if(exists...)結構外部使用變量name,這個語法在很多時候被首選使用。
默認參數
一個方法參數可以指定一個默認值。
void hello(String name="World") {
writeLine("Hello, " name "!");
}
這樣我們就不需要在調用方法時指定參數值。
hello(); //Hello, World!
hello("JBoss"); //Hello, JBoss!
默認參數必須在所有必須參數列表的最后面。
Ceylon同樣支持參數序列,使用T...語法。我們將在for循環序列中講述。
還有更多。。。
在第二部分,我們將看到如何定義一個自身類型:classes,interfaces和objects.