为何咱们须要如此多的JVM语言?html
在2013年你能够有50中JVM语言的选择来用于你的下一个项目。尽管你能够说出一大打的名字,你会准备为你的下一个项目选择一种新的JVM语言么?前端
现在借助来自像Xtext和ANTLR这样的工具的支持,建立一种新的语言比之前容易多了。个体编码者和群体受突破和改进现存JVM语言,以及传统Java的限制和缺陷的驱使,让许多新的JVM语言应运而生。java
新的JVM语言开发者感受他们的工做室针对现存语言的产物——现存的语言提供了太过受限制的功能,要不就是功能太过繁杂,致使语言的臃肿和复杂。软件开发在一个广阔的范围被应用,因而一种语言的有效性就决定于它跟特定任务领域的相关性,或者它如何在更普遍的范围中通用。全部这一切致使了资源库和框架的开发。git
大部分人大谈特谈JAVA语言,这对于我来讲也许听起来很奇怪,可是我没法不去在乎。JVM才是Java生态系统的核心啊。 程序员
James Gosling,
Java编程语言的创造者 (2011, TheServerSide)
如此多的语言存世,语言的坟场是否会饱满呢?这里有一个潜在的风险,市面上可供使用的选择太多,将会致使许多语言因为足够的关注和社区贡献而没法生存发展下去。github
然而要在这个行业生存下去,必须基于创新和创造——这些每每来自于一个从零开始,而且放弃现有的抱怨和成见,在一块白板上面起家的项目。web
这里有一条咱们将天然而然遵循的线索:现有的语言建设和框架帮助建造起来的社区支持了Java生存,而且也使得下一代Java和新的创意、结构和范式,这些东西的产生成为可能,最终将使它们的方式体如今现存的语言当中。shell
Rebel Labs的报道了概览了Java 8,Scala,Kotlin,Ceylon,Xtend,Groovy,Clojure和Fantom。可是如此多的JVM语言可供选择,咱们如何会只看中这8种选择?express
Rebel Labs 的团队就如何出这样一份报告,还有选择哪一种语言,进行了六个月的讨论。最基本的,咱们想要呈现给每一个人一些东西:Java是一种极其著名,应用普遍的语言,可是Java 8拥有许多咱们想要一探究竟的新东西。Groovy,Scala和Clojure已经找到了它们在市场中的核心竞争力,而且变得愈来愈流行起来,而像Ceylon,Kotlin,Xtend和Fantom在咱们的涉猎中相对还比较新颖,须要经受一些考察来得到信任。编程
咱们的目标是创建对每一种语言的认识,它们是如何进化的,将来将走向何方。所以在这份报告中,你将会看到咱们阐述对于这些语言的第一印象,包括那些给咱们带来震撼的特性,以及不那么能打动人的特性。
你将会看到一个HTTP服务器基础实现的源代码示例,它连接到了GitHub,所以你能够同咱们一道来探讨它。
一小段历史
最开始只存在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 languages timeline
然而,脚本设施不是惟一一个让基于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 语言 都在这:
github image
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传进去了。
1 interface Action { 2 void run(String param); 3 } 4 public void execute(Action action){ 5 action.run(); 6 }
一旦咱们拥有了一个将功能接口做为参数的方法,咱们就能够像下面这样来调用它了:
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类,可是在咱们写下这份报告时,这个类型却没有出如今最新的构建版中了:
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)
如咱们所发现的,它已经被Consumer接口替代了,而且被应用于集合资源库中的全部新方法中。例如,Collection接口中像下面这样定义了 forEach 方法:
1 public default void forEach(Consumer consumer)
2 for (T t : this) {
3 consumer.accept(t);
4 }
5 }
Consumer 接口的有趣之处在于,它实际上定义了一个抽象方法——accept(T t),还有一个防护型的方法—— Consumer chain(Consumer consumer).。这意味着使用这个接口进行链式调用是可能的。咱们尚未在JDK的资源库中找到 chain(...) 方法,所以还不怎么肯定它将怎样被应用。
并且,请注意全部的接口都标记上了@FunctionalInterface(http://download.java.net/jdk8/docs/api/java/lang/FunctionalInterface.html)运行时注解。可是除了它在运行时经过注解用javac去确认是否真的是一个功能型接口之外,它里面就不能有更多的抽象方法了。
所以,若是你编译下面这段代码:
1 @FunctionalInterface
2 interface Action {
3 void run(String param);
4 void stop(String param);
5 }
编译器将会告诉你:
1 java: Unexpected @FunctionalInterface annotation
2 Action is not a functional interface
3 multiple non-overriding abstract methods found in interface Action
而下面这段代码将会正常的编译:
1 @FunctionalInterface
2 interface Action {
3 void run(String param);
4 default void stop(String param){}
5 }
防护方法
出如今了 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);
3 }
可以使用这个循环多是由于 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> {
2 Iterator iterator();
3 public default void forEach(Consumer consumer) {
4 for (T t : this) {
5 consumer.accept(t);
6 }
7 }
8 }
添加新的默认方法并无打破源码级别的兼容性,由于接口的实现类并不须要提供它们本身对于这个方法的实现,所以从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中是两个不一样的命名空间,可是为了方便咱们可以引入动态部分中的静态命名空间:
1 mport HttpServer._ // import statics from companion object
Scala容许在任何地方使用引入(import)语句,你能够从对当前范围可见的拥有成员的任何地方,将成员引入到当前范围之中;所以Scala代码中的这个结构(指import)时间上比Java更加的经常使用,而Java只容许你将引入语句放在文件的开头,而且只能引入类或者类的静态成员。
与Java比较,还有一个鲜明出众的地方,那就是方法使用def name(argument:Type)的形式定义,而且变量也是如此定义:
1 val name: Type = initializer // final “variable”
或者是:
1 var name: Type = initializer // mutable variable
你应该不会去选择使用可变的变量,因此默认使用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") =>
2 ...
3 case method =>
4 respondWithHtml(
5 Status(501, "Not Implemented"),
6 title = "501 Not Implemented",
7 body = <H2>501 Not Implemented: { method } method</H2>
8 )
9 ...
在第二种状况下,当调用respondWithHtml时,咱们利用了(默认)已经命名的参数——它们容许咱们在调用方法的站点命名参数,以免不得不去记忆相同类型参数的顺序,或者仅仅是为了使代码更加纯净。这里咱们选择了不去命名状态,由于从Status(...)构造器的调用看来,它的意义已经很明显了。
有趣的String
另一个有趣的特性——它在Scala2.10中被加入——它是String差值。你在编写常规的String常量时带上s前缀,它就容许你在String中嵌入Scala代码,使用${}或者$来使得简单名字区分识别出来。同多行的String结合起来,咱们能容易的构造出将被发送的HTTP头部,不带任何String的串联操做:
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
(注意:咱们能够选择丢弃零参数方法的括号。)
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)
4 else if (file.isFile)
5 Some(file)
6 else
7 None
全部东西都是表达式
咱们也能利用这一事实——那就是在Scala中几乎全部的构造都是表达式——因而像if-else-if-else这种控制结构实际上就产生一个值。所以咱们可以省略掉括弧,直接让方法使用一个表达式。
在sendFile方法中咱们能够见到更多的这种东西,在里面咱们使用了本地的{...}块,它产生了一个值——这个块的最后一行表达式是它的返回值,如此咱们隐藏了块中的临时变量,而且将结果分配到块的最小的一个临时变量中。
1 val contentType = {
2 val fileExt = file.getName.split('.').lastOption getOrElse ""
3 getContentType(fileExt)
4 }
尽管这里没有展现出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(?:)都算是很棒的例子。
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相近的语法,这种语言是易学的,而且容许你编写从脚本到完整的应用程序代码,包括强大的DSL(领域特定语言)。Groovy极可能是JVM上惟一使得运行时的元编程、编译时的元编程、动态类型以及静态类型容易处理的语言。”
CÉDRIC CHAMPEAU,
Groovy中的高级软件工程师
闭包(Closure)
咱们预期Groovy将止步于句法功能,然而咱们却在文档中又发现“闭包”。为何称这超越了咱们的预期呢,由于Groovy函数值中的一等公民、更高级别的函数以及lambda表达式,这些都获得了支持。
1 square = { it * it } // ‘it’ refers to value passed to the function
2 [ 1, 2, 3, 4 ].collect(square) // [2, 4, 9, 16]
标准库对于闭包恰到好处的应用使得使用它们成为一种享受,而且也证实了它们的实力。下面是使用闭包的语法糖做为方法后面参数的好例子:
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]
集合
几乎全部的应用程序都依赖于集合。不幸的是集合大量的戳到了Java的痛处。而若是你怀疑个人这种说法,请尝试作一些有趣的JSON操做。Groovy为集合的定义将原有的语法打包,而且为了强大的可操做能力,着重使用了闭包。
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
静态类型
人们经常振奋于动态语言,由于你用不多的代码就能得到更多的功能。这经常不多被理解,而剩下的被带回去维护。所以咱们可以看到愈来愈多的动态语言得到静态类型,并且反之亦然。
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>
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写成的应用程序/框架/库是什么?还有为何你对它饱含激情?你能说出超过一种么?
“那很简单,我最感到热情的就是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
2
3 {
4 static Void main() { echo("Woah! Is it that easy?") }
5 }
然而那不是构建Fantom程序的主要方法。对于大型的项目和提早编译好模块的产品级系统,称做Pod,是使用Fantom的构建工具建立的。
构建是经过一个构建脚本编排的,它本质上是Fantom代码的另外一块。下面是HTTP服务器样本Fantom实现的构建脚本:
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 }
这里有几个事项须要注意,例如依赖规范容许咱们用比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的精彩和优雅。在最基础的层面上它提供了几种文法,像下面这样的:
1 Duration d := 5s
2 Uri uri := http://google.com
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方法,用于来回转换类型。
1 // socket is java.net.socket
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).
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 }
稍后你能够把原来的对象再取回来.
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 }
"你应该考虑吧Unsafe做为最后的杀手锏——由于它会侵蚀Fantom中的整个并发模型.若是你须要传递可变的状态 b/w Actor——你应该使用序列化——它是一个内建的特性: http://fantom.org/ doc/docLang/Actors.html#messages"
Andy Frank,
Fantom创始人
这意味着,适当的解决方案在这里就会像这样:
1 while(true) {
2
3 socket := serverSocket.accept
4
5 a := ServerActor(actorPool, socket)
6
7 a.send(“handleRequest”)
8
9 }
这样咱们就可以为接下来为对那个的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做为参数的方法的一种类型.
1 Str // never stores null
2 Str? // might store null
经过这种方式,代码不会受到空(null)检查的污染,而且同Java的互操做变得更加的简单了.
值得一提的还有另一种特性.那就是带有变量插值(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
参数能够有默认值,支持混合和声明式编程,还有运算符重载.每同样东西都各得其所.
只有一件东西咱们没有看到并感到是一种缺陷,那就是元组(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开发者来讲,这无疑带来更新鲜、更交互的方式来写代码。
jvm languages code example
函数式编程——另一种思考方式
Clojure是一种很是相似于Lisp和Scheme的函数式编程语言.函数式范式同那些习惯于Java的面向对象方式而且习惯于其反作用的方式很是不一样.
函数式编程推崇:
不多或者彻底没有反作用
若是使用相同的参数区调用,函数就永远返回同一个结果(而不是依赖于对象状态的方法)
没有全局变量
函数是第一位的对象
表达式懒计算(Lazy evaluation of expression)
这些特性并非Clojure独有的,而是整体上对函数式编程都要求的:
1 (defn send-html-response
2 "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"))
8
9 ))
同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)
4 (java.util Date)
5 (java.io PrintWriter BufferedReader InputStreamReader BufferedOutputStream)))
建立和调用Java对象是很是直截了当的.而实际上有两种形式(在这个优秀的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
下面是一个实际的样例:
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))))
然而,对于一些结构,咱们决定要使用到Clojure的方式.原生的Java代码使用StringTokenizer,这样作违背了不可变对象的纯函数式原则,还有无反作用的原则.调用nextToken()方法不只有反作用(由于它修改了Tonkenize对象)而且使用同一个(或许是不存在的)参数也会有不一样的返回结果.
因为这个缘由,咱们使用Clojure的更加"函数式"的Split函数:
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 [...]
并发(Concurrency)
Clojure从一开始设计对并发很上心,而不是过后诸葛亮.使用Clojure编写多线程应用程序很是简单,由于全部的函数默认都实现了来自Java的Runnable和Callable接口,自身得以容许其任何方法在一个不一样的线程中运行。
Clojure也提供了其它特别为并发而准备的结构,好比原子(atom)和代理(agent),可是咱们在这个HTTP服务器示例中并无使用它们,而是选择熟悉的Java的Thread.
1 (defn new-worker
2 "Spawn a new thread"
3 [client-socket]
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';
3 import java.base '7';
4 }
这里的意思是这个模块叫作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()方法来启动咱们的应用:
1 void run(){
2 print("Hello from Ceylon!");
3 }
Ceylon中的方法(或者函数)能够是单独的,而不属于任何一个类。属性(attribute)也一样适用这一特性。实际上,发现它编译成了什么是至关有趣的一件事。每个单独的属性(或域)和方法都被编译成了一个专用的类。那意味着下面的这段代码被编译成了两个类,port_.class和run_.class——它们也一样包含了公共静态返回值为空的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 }
在这个过程当中咱们了解到,Ceylon工具编译和打包了全部东西到Ceylon ARchive(.car)中,而且在那以后对自身作了清理,因此你不会在编译以后找到class文件——咱们只有解压这个压缩了的存档才能访问class文件。
工具的一个很棒的部分是,当一个新的依赖被加入到module.ceylon文件中时,依赖会自动从CeylonHerd网站下载,而且由IDE处理识别。那是这个语言的基础设施的另一个长处。
同Java的互操做性
同Java的互操做性对我来讲是一个棘手的部分。首先,你必须将依赖添加到module.ceylon中,使得程序可以使用Java类:
1 import java.base '7';
当你在Ceylon中使用Java类时,你可能会直观的指望着能够像在Java程序中同样正常的使用Java类的实例中的全部方法。可是在Ceylon中不是这样的。下面是一个实例:
1 value printWriter = PrintWriter(socket.outputStream);
你但愿能在socket上面使用getOutputStream()方法,可是Ceylon将它解释成了一个域的获取器(getter),而且用一个属性访问修饰(大概是?)来取代它的语义。尽管当你检查它的来源时,getOutputStream()并非一个获取器。
jvm languages screenshot
类型转换是咱们须要面对的第二个挑战。Ceylon类型和Java类型之间有一种特殊的映射。
所以fileData必定是一个字节数组类型,而根据类型映射规则,它应该在Ceylon中被声明为一个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)
显然,为了让类型转换能正常的工做,咱们不得不 (在module.ceylon中)导入特殊的Java互操做模块:
1 import ceylon.interop.java '0.4.1'
而后才有可能使用由互操做模块提供的帮助方法。下面就是类型转换所须要的代码:
1 Array<Integer> fileData = createByteArray(fileLength);
2 value fileIn = FileInputStream(file);
3 fileIn.read(fileData);
"事实上这是一个真正使人痛苦的角落。咱们处理Java原生类型的通常性策略在处理器容器类型时是不健全的,这一般是很好的,由于Java的泛型并非基于Java原生类型的抽象:你不能拥有一个List。可是当Java有了数组(array)时,它们是特殊类型的容器类型,是基于原生类型的抽象,咱们所以须要用特殊的方法处理这种特殊的状况。这个问题将会在将来版本的编译器中被修复。"
Gavin King
Ceylon创始人
基本上,毕竟HTTP服务器实现的全部问题都已经在运行着了,可是在运行时会出现惊喜:null检查。
如今应用程序启动了,可是在客户端提交了一个GET请求之后,它失败了,打印以下的调用栈:
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)
这个使人惊奇的部分位于classes.ceylon第19行,这里有这样一行代码:
1 String input = bufferedReader.readLine();
这里是一些如何使用javap帮助来阅读反编译代码的知识。字节码透露了Ceylon编译器在Java类的方法返回以后插入了null检查。所以要解决这个问题,咱们不得不在声明中使用?后缀:
1 String? input = bufferedReader.readLine();
可是随后就会有这个值是如何被使用的问题。例如,若是它是做为StringTokenizer的一个参数使用,那么初始化这个类应该也会失败。所以就有了下面这种蹩脚的解决办法:
1 if(!exists input){
2 return;
3 }
确定应该有处理这种状况的更好方式,而不是像眼前这样就处理了问题。
"若是有你确实不想在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的组合容许你对变量进行切换,但当你可以得到一种更加丰富的参与时,就不只仅只使用它的值了。你能够考虑像下面这样:
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 }
若是你能想象你将会怎样在Java中实现这个示例,你就会以为上面的代码有多干净优雅了。有趣的是C#也有相似的is和as的使用方式,一样也实现了可为空(nullable)类型。
利用参数命名(parameter naming)的方法调用也是默认的。这种问题也能在你须要调用一个拥有五个布尔值参数的方法,确保你传入的true和false顺序正确时,让你感到头痛。参数命名经过在方法调用上带上参数名称来绕过参数的混淆。这样很棒。这在过去已经被其余的语言和脚本框架再一次作到了,它们容许当用户乐于接受一些默认参数值时,方法调用能够省略掉某些参数。让咱们来看一个例子:
1 private fun print(out : PrintWriter,
2 pre : String,
3 contentType: String = “text/html”,
4 contentLength : Long = -1.toLong(),
5 title : String, body : () -> Unit)
这里咱们有了一个带有若干个String参数的方法,还有一个做为输入的函数。它使用下面的方式来调用。请注意contentType和contentLength参数都被省略了,意味着声明中的默认值将被使用。
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 ")})
那么这有什么意义呢?这将省掉不少方法重载!虽然言重了一点,可是一想到Java在过去超过20多年时间里没有像这样的好东西出世,就足以让这个功能使人惊奇了。有时候就有一点点感受你是在暗无天日的编写着代码。加点油吧,Java,你得奋起直追才行!
Kotlin将帮助你编写安全的代码,除非你不想这样作
首先,你能够对NPE(空指针异常)说再见了!Kotlin使用“可为空(nullable)类型”和“不可为空(non-nullable)类型来将可能会为空的变量和永远不会为空变量进行区分。考虑下面的代码:
1 var a : String = "a"
2 a = null // compilation error
为了容许为空,变量必须被声明为nullable,在这个场景中,就写上 String?:
1 var b : String? = "b"
2 b = null // valid null assignment
如今,若是你调用一个使用了变量a的方法,它会担保不会出现NPE,所以你能够安全的说:
1 val l = a.length()
可是若是你使用b调用同一个方法,那就不怎么安全了,编译器会报告说出了一个错误:
1 val l = b.length() // error: variable 'b' can be null
经过知晓哪些变量可为空,Kotlin编译器负责当你解绑一个可为空的引用时,你能够使用下面的方法其中之一来作到:
Kotlin中的安全调用同Groovy中那些功能很是类似,包括记号(notation)语法。经过像下面这个例子同样使用‘.?’解绑一个可为空类型,告诉编译器调用对象b上面的length方法,而一旦b为空,就不作这一步调用length的操做。
1 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)和关键词,留给咱们一个可读性更好的,更加干净的实现.让咱们来看看一些代码:
1 package com.zt
2 class Oppa {
3 def static void main(String[] args)
4 {
5 println('Gangnam, Style')
6 }
7 }
请注意咱们不喜欢用"hello world"这个例子,由于你不多从它们这样的例子里得到更多的东西,所以这里使用咱们的"江南(Gangham)Style"示例:)到目前为止咱们看到同Java相比极少有改变,但咱们须要注意到的是IDE中有一个xtend-gen源代码文件夹,它里面有从Xtend代码翻译的Java代码:
1 package com.zt;
2 import org.eclipse.xtext.xbase.lib.InputOutput;
3 @SuppressWarnings("all")
4 public class Oppa {
5 public static void main(final String[] args) {
6 InputOutput.println("Gangnam, Style");
7 }
8 }
有趣的是它经过本身实现的InputOutput类来进行输出。
回顾Xtend HTTP server 样例, 咱们写了137行代码, 被翻译成309行Java代码。被建立的Java代码不太可读而且也不是很顺利的编辑, 因此我不确信为何这被显示。
jvm languages report - xtend screenshot
有趣的是, 在咱们开始编译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){
02 val html =
03 '''<HTML>
04 <HEAD><TITLE>«title»</TITLE></HEAD>
05 <BODY>
06 «body»
07 </BODY>
08 </HTML>'''
09 socket.sendHttpResponse(status, "text/html", html.toString. getBytes("UTF-8"))
10 }
这意味着咱们实际上能够在Socket对象上面调用sendHtmlResponse方法.
1 socket.sendHtmlResponse(
2 "HTTP/1.0 404 Not Found",
3 "File 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名字的由来之处.
在咱们第一个代码块中,咱们使用了下面这种结构:
1 '''<HTML>
2 <HEAD><TITLE>«title»</TITLE></HEAD>
3 <BODY>
4 «body»
5 </BODY>
6 </HTML>'''
这被称做模板表达式,被用在了许多其余的语言上.它容许咱们很容易的建立HTML,而不用去磕磕绊绊的使用out.print()语句,从而容许咱们更好的考到咱们建立的文档是什么样的.你会注意到咱们能够在语句中标记了变量,而且你也能够在模板中使用条件控制结构.
咱们也会想要其余语言中默认使用的命名参数,咱们坚持认为无论如何,它都是全部语言的一部分!它(Xtend)确实使用了隐式类型,尽管这样还不错.语言自己仍然是静态类型的,可是变量的类型是在编译的时候由表达式将会赋予的值来决定的.例如:
1 val WEB_ROOT = "."
2 val DEFAULT_FILE = "index.html"
3 var Socket socket
在这个例子中,开始的两个变量都明确是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 logo
Java仍然是活蹦乱跳的!咱们能够预见lambda和伴随的特性(防护方法,升级的集合库)将很快在Java中产生重大影响.语法至关不错并且一旦开发者意识到这些特性可以带给他们生产力的价值,咱们将在将来看到许许多多利用这些优点的代码.
scala logo
咱们看到,Scala可以很是富有表现力,而且相比Java,它能够容许咱们离开许多样板语法。这是一个真正的优点,虽然咱们的HTTP服务器的例子可能表达不出来,但这也许能给你一些想法来了解这个语言是什么样子。关键的一点,就是拥抱不变性和尝试象可被组成的表达式同样塑造代码。
groovy logo
做为Java开发人员,Groovy很容易上手,并是Web应用程序,DSL,模板和脚本引擎的一个很棒的选择。它处理了在Java中常见的烦恼,包装了特性的现代集合,并有一个很棒的标准库。Groovy会继续被普遍采用, 是JVM的动态语言的一个很好选择。咱们只是怕它在特征集持续增加的同时失去焦点。
fantom logo
Fantom是一个成熟和稳定的编程语言有着很是优美的API。 咱们绝没有说咱们已经完美了, 若是能够使Fantom更好 ,你能够改善它: 咱们 Github 仓库对comments 和 pull requests开放。若是你只是观其表面,你只能经过语法来判断编程语言。 Fantom 有着类Java的语法,没有分号而且会抛出了一些自身的关键字。不然, 这确定是一个来源于Java的优秀语法。
clojure logo
在JVM上面,Clojure 提供了强大函数式语言平台上。若是开发有Lisp/Scheme经验,那是很是爽的一种方式。然而,Java开发者不只仅是面对新的语法并且新的范式。
尽管它会被滥用相似像JAVA那样,那不是最优的。在未来,Clojure 的强大之处在于并发方面会提供不少便利。愈来愈多的程序员都会感兴趣代码的并行之处。
ceylon logo
Ceylon的语法和关键字对Java而言,很是的不同。然而,它不会困惑咱们多少。 在一些场景下面, Ceylon和Java相关性很强。对实例化而言, 'satisfies' 在Ceylon和 'implements' 在Java想对应。
总的来讲,有一个至关有趣的经历。咱们对Ceylon的印象是汇集OOP和基础设施(模型系统)风格,而不是简短的表达式。 不要尝试百万变成一行。Ceylon创造者担忧Java语言的冗长,重点在于生态系统和语言设计。
kotlin logo
总的来讲,Kotlin 是一门友好的语言。尽管Kotlin没有特别的创造性,它的优点在于吸取别语言最精华的部分。
该语言很是友好去使用,具备函数,参数名,默认值,扩展类等特色。 它也是一个漂亮的与语言! 关键字的比例:在Kotlin中,代码在构造的时候例如,'when','is'和'as'更加好。
每一个2-3三月,发布一个正式版本,Scala开发者可能会转向去使用Kotlin这门语言。尽管咱们认为Scala用户不会太有兴趣。
xtend logo
Xtent提供了Java只承诺在将来实现但目前缺少的简洁明了的功能。问题是,咱们会建议让别人在Java或其余的JVM语言之上使用它吗?
然而,对Java用户而言这取决于这些好处能持续多久,一旦Xtend中附加的的这些这些不错的功能在Java中像lambda和扩展的方法同样,被实现了的话,咱们不相信有足够的理由转移到Xtend上来。咱们能想象使用其余诸如Scala的JVM语言的开发者将Xtend看作Java n + 1或着带有一种插件的Java,而不是一种打破了全部界限的语言,所以咱们不会理解为何Scala的用户可能会破天荒的去使用Xtend。