Xtend官方文档-第一部分

第一章  入门

第一节 简介

Xtend是一种静态类型的编程语言,可以转化为可理解的Java源代码。其语法和语义构成基于Java编程语言,但在许多方面得到改进:

扩展方法 - 增强封闭类型新功能

Lambda表达式 - 简洁的匿名函数常量语法

ActiveAnnotations (积极的注解)- 注解处理的增强因子

运算符重载 - 使您的库更具表现力

强大的开关表达式 - 隐式的类型转换

多重调度 - 又名:多态方法调用

模板表达式 - 具有智能空白处理

没有语句 - 一切都是表达

属性 - 简化了getter和setter的访问

类型推断 - 可以少写类型签名

完全支持Java泛型 - 包括所有一致性和转换规则

翻译为Java而不是字节码 - 了解发生了什么,并可将代码用于Android或GWT等平台

与其他JVM语言不同,Xtend 与Java 具有零互操作性问题:您编写的所有内容都与预期的Java代码完全一致。与此同时,Xtend更加简洁、可读和表现力。Xtend的小型库只是一个薄层,提供有用的实用工具和在顶层提供Java开发工具包(JDK)的扩展。

当然,也可以通过Java来完全透明地调用Xtend方法。此外,Xtend还提供现代风格的、基于Eclipse的IDE,并与Eclipse Java开发工具(JDT)紧密集成,包括调用层次结构、重命名重构、调试等功能。

第二节  Hello,World

就像你看到看到其他任何语言一样,第一件事是Hello World的例子。在Xtend中,它代码为:

    class HelloWorld {

      def static void main(String[] args) {

        println("Hello World")

      }

    }

Xtend看起来和Java非常相似。乍一看,主要区别似乎是def关键字来声明一个方法。同样在Java中,必须将类和main方法定义为应用程序的入口点。诚然,Hello World程序没有体现出Xtend的优势。可当你让程序真正的做一些事情时,真正的表现力将会释放出来,这将会在下一秒学到。

Xtend类驻留在普通的Eclipse Java项目中。一旦安装了SDK,Eclipse将自动将所有类转换为Java源代码。默认情况下,您将在源代码文件夹xtend-gen中找到它。hello world示例被转换为以下Java代码:

    // Generated Java Source Code

   import org.eclipse.xtext.xbase.lib.InputOutput;

    public class HelloWorld {

       public static void main(final String[] args) {

          InputOutput.<String>println("Hello World");

     }

   }

生成的Java代码中唯一令人惊讶的可能是引用库的InputOutput类。它是运行时库(runtime library )的一部分,一个很好的工具,可以非常方便地在表达式中使用。

您可以将Xtend类放入任何EclipseMavenJava项目源文件夹中。如果项目尚未正确配置,Eclipse告诉你丢失的库。xtend.lib必须在类的路径上。IDE将提供一个快速修复来添加它。

下一件事是,在工作区(workSpace中,实现一个示例项目。在在Eclipse导航(Navigator)视图任意位置单击鼠标右键,并选择New新建)→Example示例)...

在出现的对话框中,您将找到Xtend的两个示例:

Xtend介绍示例(Xtend Introductory Examples),包含几个示例代码片段,说明Xtend的某些方面优势。例如,它显示了如何允许编写如下代码构建API

     assertEquals(42.km/h, (40_000.m + 2.km) / 60.min)

下一节中详细介绍的电影示例也包括在那里。

Xtend的Euler解决方案(Xtend Solutions For Euler,您可以在Project Euler中找到一些问题的解决方案。这些例子利用了Xtend的整体表现力。例如Euler问题1可以用这个表达式来解决:

     (1..999).filter[ i | i % 3 == 0 || i % 5 == 0 ].reduce[ i1, i2 | i1 + i2 ]

第三节 电影示例

电影示例包含在示例项目Xtend Introductory Examplessrc / examples6 / Movies.xtend)中,是读取关于电影的数据文件,并对其进行一些分析。

数据

电影数据库是纯文本文件(data.csv,具体描述电影的数据集。以下是一个示例数据集:

    Naked Lunch  1991  6.9  16578  Biography  Comedy  Drama  Fantasy

值由两个空格分隔。列是:

1.标题(title

2.年份(year)

3.评级(rating)

4.票数(numberOfVotes)

5.类别(categories)

让我们定义一个表示数据集的数据类Movie

@Data class Movie {

  String title

  int year

  double rating

  long numberOfVotes

  Set<String> categories 

}

电影类是POJO,数据集中的每列都有强类型字段。@Data解将会把类变成一个不变值类,那么它会得到:

l 每个字段的getter方法,

l 一个hashCode()/ equals()实现,

l Object.toString()现,

l 构造函数按所有字段的声明顺序接受值。

解析数据

现在让我们在同一个文件中添加另一个类,并一个名为movies的字段,初始化为列表。我们解析文本文件并将数据记录转换为Movie类:

import java.io.FileReader

import java.util.Set

import static extension com.google.common.io.CharStreams.*

class Movies {

  

  val movies = new FileReader('data.csv').readLines.map [ line |

    val segments = line.split('  ').iterator

    return new Movie(

      segments.next, 

      Integer.parseInt(segments.next), 

      Double.parseDouble(segments.next), 

      Long.parseLong(segments.next), 

      segments.toSet

    )

  ]

}

字段的类型可以从右侧的表达式来推断。这被称为局部类型推断,在Xtend中随处可见。我们希望该字段是final,所以我们使用关键字val,将其声明为一个值变量(只读变量)。

右侧的初始化,首先创建一个新的FileReader。然后在该实例中调用方法readLines()。但是如果你看看FileReader,你不会找到这个方法。事实上,readLines()是来自Google GuavaCharStreams的静态方法,它被导入作为扩展( extension)。扩展允许我们使用这种可读的语法。

import static extension com.google.common.io.CharStreams.*

CharStreams.readLines(Reader)使用另一个扩展方法map返回List<String>类型,其在运行时库中定义的(ListExtensions.map(...)),并自动导入,因此可用于所有列表。该map扩展需要一个函数作为参数。它基本上为列表中的每个值调用该函数,并返回另一个包含函数调用结果的列表。实际上,这个映射是懒惰地执行的,如果你不访问列表的结果值,那么映射函数就永远不会被执行。

而函数对象是使用lambda表达式(方括号中的代码)创建的。在lambda中,我们处理文本文件中的一行,并通过使用两个空格字符作为分隔符作为分割符,将其转换为movie。对于拆分操作的结果,调用iterator()方法。您可能知道String.splitString返回一个字符串数组(String[]),当我们调用Iterable.iterator()时,Xtend自动转换为列表

val segments = line.split('  ').iterator

现在我们使用迭代器,为每个产生的String 创建一个Movie实例。数据类型转换,通过从包装器类型调用静态方法来完成(例如String转为int)。Iterable的其余部分,变成一组类别(categories)。因此,迭代器调用扩展方法IteratorExtensions.toSet(Iterator<T>),以完成其余值的转换。

return new Movie (

  segments.next, 

  Integer.parseInt(segments.next), 

  Double.parseDouble(segments.next), 

  Long.parseLong(segments.next), 

  segments.toSet

)

响应查询

现在我们已经将文本文件解析成了List<Movie>,我们已经准备好对它执行一些查询了。我们使用JUnit来使单个查询可执行并确认其结果。

查询1:动作片数量是多少?

@Test def numberOfActionMovies() {

  assertEquals(828, 

    movies.filter[ categories.contains('Action') ].size)

}

首先,对movies过滤(filter)。lambda表达式检查当前影片的类别是否包含条目'Action'。请注意,不像我们用来将文件中的行转换成movieslambda,这次我们没有声明一个参数名。所以上面表达式,我们可以这样写:

movies.filter[ movie | movie.categories.contains('Action') ].size

但是由于我们省略了参数名称和垂直条|,所以变量被自动命名itit是一个隐式变量。它的用法类似于隐式变量this所以也可以写成这样:

movies.filter[ it.categories.contains('Action') ].size

或甚至更紧凑地写成前面那样:

movies.filter[ categories.contains('Action') ].size

最后我们size也会调用生成的iterable,这也是一个扩展方法。它在实用程序类(utility class )IterableExtensions定义。

查询2:80年代最好的电影是哪一年上演的?

@Test def void yearOfBestMovieFrom80s() {

  assertEquals(1989, 

    movies.filter[ (1980..1989).contains(year) ].sortBy[ rating ].last.year)

}

在这里,我们过滤1980年至1989年(80年代)范围内的所有电影。操作符“..”又是一个在IntegerExtensions定义的扩展,并返回IntegerRange的实例。操作符重载在第二章中说明。

由此产生的迭代排序,则IterableExtensions.sortBymoviesrating进行排序。由于它按升序排列,我们从列表中取出最后一个电影并返回其year

我们也可以按降序排序,还可以列出列表的头:

movies.filter[ (1980..1989).contains(year) ].sortBy[ -rating ].head.year

另一个可能的解决方案是颠倒排序列表的顺序:

movies.filter[ (1980..1989).contains(year) ].sortBy[ rating ].reverseView.head.year

请注意,首先排序,然后取最后或第一个,代价稍大一点。我们可以使用方法reduce更有效地找到最好的电影。你可以自己尝试一下。

前面的例子调用movie.year以及movie.categories,实际上是访问相应的getter方法

查询3:前两部电影的得票总数是多少?

@Test def void sumOfVotesOfTop2() {

  val long sum = movies.sortBy[ -rating ].take(2).map[ numberOfVotes ].reduce[ a, b | a + b ]

  assertEquals(47_229L, sum)

}

首先,电影按评级排序,然后我们取最好的两个。接下来,使用map函数将电影列表的numberOfVotes形成新的列表。现在我们有List<Long>,可以通过添加值将其缩小到一个Long

你也可以用reduce代替mapreduce。你知道怎么做吗?

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值