这是Ceylon语言系列文章的第一部分。要注意的是语言的特性可能在最终版发布之前发生改变。
Ceylon 是一门新的语言,它运行在Java虚拟机上,目前正有我所在的小组开发,它隶属于RedHat。我们都是Java和Java生态系统的粉丝,因为它的实用 性、广阔的文化氛围和开发社区、天生适用于商业应用以及可移植性。然而我们必须承认这门语言和其现有的类库,已经过了15年的发展,它不能再提供更好的功 能来解决现在的商业问题。
最重要的是,Ceylon的设计目的是让大型团队更好的协作
Ceylon编译器还没有完成,所以你还不能使用Ceylon编写代码。不管怎样,我愿意让社区参与语言和SDK的开发,所以这个系列文章是给对Ceylon感兴趣的人一个提前预览。
让我们从头开始吧。
这是一个经典的例子程序。
这个方法在控制台打印出“Hello, World!”。这是一个顶层方法就像C语言函数--它直属于包含它的包中,它不是一个任何类型的成员。你不需要接收一个对象来调用顶层方法,你只要像下面这样就可以调用它:
Ceylon没有Java风格的静态方法,但是你可以使用顶层方法来充当同样的角色。静态方法存在的问题是它打乱了程序的块结构。Ceylon拥有严格的块结构--一个内嵌的块总是可以在所有包含它的块中被访问。这与Java的静态方法不同。
这个方法使用了void关键字,这表示方法没有返回值。当方法被执行时,它调用另一个顶层方法writeLine(),这个方法在控制台中显示它的参数。
连同void关键字,还有一个命名为Void的类型,其再任何void方法中被返回,这也是Ceylon类型系统的根类型。
可能对void方法有一个返回类型很不解。对此的解释就是Ceylon内所有的方法都是函数。(但未必都是函数--比如hello(),一个Ceylon函数能够有边缘效应。)
在 一个非常抽象层里,每个方法都接受参数并有返回结果。对于Void类型,简单来说就是提供表示未知值和未知类型的一种途径。你可以给Void分配任何值, 包括null,但是它无法被再次回收,即使知道它的类型值是什么。因此理论上void方法有返回值,只是我们无法知道关于它值的任何信息。现在这听起来可 能没什么用处,但是当我们讨论first-class函数和Ceylon类型安全的元数据模型时将证明其非常有用。
通常给像hello()这样重要的方法上添加注释文档是一个好习惯。一个方式就是使用C语言风格的注释,就像下面这样:
或者像这样:
但是更好的方式是使用doc注解来写注释。
doc注解包含的文档被包含在Ceylon文档编译器输出中。文档编译器还将支持其它几个注解,包括by,用来指定程序作者;see,用来关联其它代码元素;以及throws,报告用户程序执行抛出的异常类型。
同样还有一个deprecated注解,用来说明程序元素在将来的版本中会被移除。
注意,当一个注解的参数是字面原文形式,那么久不需要有关闭括号。
像doc,by,see和deprecated这类的注解不是一个关键字。它们只是普通的标识符。同样的对于语言中定义的注解:abstract,variable,shared,formal,actual和friends也都不是关键字。但void是关键字。
Hello World程序--现在广泛流行--提供非常有限的用户体验。一个更典型的例子在不同的运行时产生不同的输出。
让我们来询问我们的程序,以便它告诉我们更多关于它自己的信息。
我们能看到Ceylon的字符串提供的两个好处。第一就是可以分割字符串为多行,这对于在doc注解中写文档非常有帮助。第二个就是我们可以在字符串内部插入表达式,从技术上来讲,一个带有表达式的字符串不在是一个真正的字符串了,而被看做一个字符串模板。
一个字符串表达式必须开头和结尾都必须是字符串,下面的语法是错误的:
在最后加上一个空的字符串就能修正上面例子的错误。
注意,在Ceylon中这不是唯一连接字符串的方法。其实这只是对于在不变的字符串中插入变量或表达式有用。+操作符可作为另外一种选择,还有更多灵活的例子:
但 是不要看到这两种方式的输出是一样的就认为它们是等价的。+操作符仅仅是对表达式求值并产生一个不可变的String对象。而String模板是一个 Gettable<String>的表达式,其不马上对内插表达式求值(有点像Hibernate里的懒加载)。如果你打印String模板 对象两次,你可能会看到两个不同的输出。其实,String模板是一种closure(闭包)--一个重要的概念,我将在后面介绍。
大多数程序都要求接收输入并产生输出,并且程序依赖于接收的输入。当然,这么做对用户来说有点苛刻,但是,一些额外的工作必须做!
对此,提供一个改良版本的Hello World程序,其从命令行接收一个名字作为输入。我们必须考虑在命令行什么都没输入的情况,这样就能给我们一个机会去体验Ceylon如何处理Null值,这可与Java于C#的处理方式有着很大的不同。
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。
null值关联一个Nothing类型的实例,但是它不是一个Object的实例。因此这是一种简单分配本地变量为Null(不是一个可选类型)的方式。
Ceylon编译器也不允许你最T类型的值做任何“危险”的事情,在Java里这样会抛出NullPointerException。if(exists...)结构让我们从X?类型提取X类型的值,从而允许我们调用X值的方法。
事实上,没有可能在一个可选类型的表达式里使用==操作符,你不能像在Java中那样使用if(x==null)。这有助于避免像Java中的不良操作==,x==y在Java中如果x和y都是null将返回true.
在if(exists...)条件中可以声明本地变量name:
自从我们不能再if(exists...)结构外部使用变量name,这个语法在很多时候被首选使用。
一个方法参数可以指定一个默认值。
这样我们就不需要在调用方法时指定参数值。
默认参数必须在所有必须参数列表的最后面。
Ceylon同样支持参数序列,使用T...语法。我们将在for循环序列中讲述。