Groovy 3 亮点

作者:Paul King
发布时间:2020年2月13日 上午2:28


一般改进

Groovy 既具有动态特性(支持类似于 Ruby 和 Python 的代码风格),也具有静态特性(支持类似于 Java、Kotlin 和 Scala 的风格)。Groovy 继续改进这两种特性 - 填补任何功能空白。例如,Groovy 有许多更好的管理空值的工具。您可以使用 Groovy 的空安全导航运算符,借助 Java 的 Optional 或为类型检查器提供空检查扩展。在 Groovy 3 中,这些功能得到了增强,增加了对数组、列表和映射的空安全索引,以及一个新的 AST 转换 @NullCheck,用于自动为代码添加空检查。

总的来说,该语言设计很大程度上借鉴了 Java,因此我们会密切关注 Java 的变化,并在适当时采取相应的行动。为了让 Groovy 为 Java 模块做好准备,并使其与 JDK 9-15 版本良好配合,我们做了很多工作。其他工作则大幅提高了字节码生成的性能,它利用了 JVM 调用动态功能。Groovy 4 中已经在进行进一步改进这些领域的更改。

在底层还有许多其他性能改进。编译期间的类型解析效率更高,并且为许多场景生成了更高效的字节码。Maven BOM 的添加允许从其他项目更灵活地使用 Groovy。

Groovy 在脚本、测试、编写领域特定语言 (DSL) 以及金融计算和数据科学等领域也具有独特的优势。我们一直在努力确保这些优势得到保持。用于高精度数字的精度得到了提高,并且是可配置的。Groovy 控制台和 groovysh 等许多工具也得到了改进。

Groovy 的其他关键优势,如其运行时和编译时元编程功能,也进行了许多小的增强。总而言之,此版本代表了几年来工作成果的结晶。自 Groovy 2.5 以来,已经添加了 500 多个新功能、改进和错误修复。下面只讨论一些亮点。

Parrot 解析器

Groovy 有了一个新的解析器。虽然这主要是 Groovy 内部的一个变化,但对用户来说,好消息是新的解析器更加灵活,如果需要,可以更快地更改语言。

新语法

新的解析器为我们提供了一个添加一些新语法特性的机会

  • !in 和 !instanceof 运算符

    assert 45 !instanceof Date
    assert 4 !in [1, 3, 5, 7]
  • Elvis 赋值运算符

    def first = 'Jane'
    def last = null
    first ?= 'John'
    last ?= 'Doe'
    assert [first, last] == ['Jane', 'Doe']
  • 身份比较运算符

    assert cat === copyCat  // operator shorthand for "is" method
    assert cat !== lion     // negated operator shorthand
  • 安全索引(用于映射、列表和数组)

    println map?['someKey'] // return null if map is null instead of throwing NPE

Java 兼容性

Groovy 语法可以被认为是 Java 语法的超集。在适当的时候使用 Groovy 提供的增强功能被认为是一种良好的风格,但 Groovy 的目标是仍然尽可能多地支持 Java 语法,以便于从 Java 迁移或方便同时使用 Java 和 Groovy 的人员进行切换。

新的解析器提供的灵活性使得可以填补几个语法兼容性漏洞,包括

  • do/while 循环

    def count = 5
    def factorial = 1
    do {
        factorial *= count--
    } while(count > 1)
    assert factorial == 120
  • 增强的经典 Java 风格 for 循环(参见多赋值 for 循环示例;请注意 for 语句最后一个子句中的逗号)

  • 与 for 循环结合使用的多赋值

    def count = 3
    println 'The next three months are:'
    for (def (era, yr, mo) = new Date(); count--; yr = mo == 11 ? yr + 1 : yr, mo = mo == 11 ? 0 : mo + 1) {
        println "$yr/$mo"
    }
  • Java 风格的数组初始化(但您可能更喜欢 Groovy 的字面量列表表示法)

    def primes = new int[] {2, 3, 5, 7, 11}
  • Lambda 表达式(但您可能经常更喜欢 Groovy 的闭包,它支持蹦床/尾递归、部分应用/柯里化、记忆化/自动缓存)

    (1..10).forEach(e -> { println e })
    assert (1..10).stream()
        .filter(e -> e % 2 == 0)
        .map(e -> e * 2)
        .toList() == [4, 8, 12, 16, 20]
    def add = (int x, int y) -> { def z = y; return x + z }
    assert add(3, 4) == 7
  • 方法引用(但您可能经常更喜欢 Groovy 的方法指针,它是具有前面提到的优点的闭包)

    assert ['1', '2', '3'] == Stream.of(1, 2, 3)
            .map(String::valueOf)
            .toList()
  • “var”保留类型(即使使用 JDK 8 也允许使用 Java 10/11 功能)

    var two = 2                                                      // Java 10
    IntFunction<Integer> twice = (final var x) -> x * two            // Java 11
    assert [1, 2, 3].collect{ twice.apply(it) } == [2, 4, 6]
  • ARM Try with resources(Java 7 和 9 变体在 JDK 8 上工作 - 但您可能更喜欢 Groovy 用于资源的内部迭代方法)

    def file = new File('/path/to/file.ext')
    def reader = file.newReader()
    try(reader) {
        String line = null
        while (line = reader.readLine()) {
            println line
        }
    }
  • 嵌套代码块

  • Java 风格的非静态内部类实例化

  • 接口默认方法(但您可能更喜欢 Groovy 的特性)

    interface Greetable {
        String target()
        default String salutation() {
            'Greetings'
        }
        default String greet() {
            "${salutation()}, ${target()}"
        }
    }

拆分包更改

为了使 Groovy 的模块化 jar 成为一流的模块,几个类已经移动了包。一些例子

groovy.util.XmlParser => groovy.xml.XmlParser
groovy.util.XmlSlurper => groovy.xml.XmlSlurper
groovy.util.GroovyTestCase => groovy.test.GroovyTestCase

在大多数情况下,Groovy 3 中同时提供了旧类和新类。但在 Groovy 4 中,旧类将被删除。有关这些更改的完整列表,请参阅发行说明

DGM 改进

Groovy 为现有的 Java 类添加了许多扩展方法。在 Groovy 3 中,添加了大约 80 个新的此类扩展方法。我们在这里只重点介绍几个

  • 数组和可迭代对象上的 average()

    assert 3 == [1, 2, 6].average()
  • String、CharSequence 和 GString 上的 takeBetween()

    assert 'Groovy'.takeBetween( 'r', 'v' ) == 'oo'
  • 数组和可迭代对象上的 shuffle()shuffled()

    def orig = [1, 3, 5, 7]
    def mixed = orig.shuffled()
    assert mixed.size() == orig.size()
    assert mixed.toString() ==~ /\[(\d, ){3}\d\]/
  • Future 上的 collect{ }

    Future<String> foobar = executor.submit{ "foobar" }
    Future<Integer> foobarSize = foobar.collect{ it.size() } // async
    assert foobarSize.get() == 6
  • LocalDate 上的 minus()

    def xmas = LocalDate.of(2019, Month.DECEMBER, 25)
    def newYear = LocalDate.of(2020, Month.JANUARY, 1)
    assert newYear - xmas == 7 // a week apart

其他改进

改进的注解支持

最新版本的 Java 允许在更多地方使用注解 (JSR308)。Groovy 现在也支持此类用例。这对 Spock、Micronaut、Grails、Jqwik 等框架以及其他框架非常重要,并且为其他 AST 转换(Groovy 的一个关键元编程特性)打开了大门。

Groovydoc 增强

除了 Groovydoc 支持新的解析器外,您现在还可以通过各种方式嵌入 Groovydoc 注释

  • 可以将它们放在 AST 中,供 AST 转换和其他工具使用。

  • 以特殊 /**@ 开头注释分隔符开头的 Groovydoc 注释也可以嵌入到类文件中。这提供了一个在 Groovy 中的功能,其灵感来自于 Ruby 等语言,这些语言可以将文档嵌入到标准二进制 jar 中,因此始终可用,而无需依赖于单独的 javadoc jar。

获取 Groovy

官方源代码版本在下载页面上。您可以在该页面上找到便捷的二进制文件、可下载的文档、SDK 包以及指向各种社区工件的指针,以及允许您验证安装的信息。您可以在任何支持 Java 的平台上使用 zip 安装,或者考虑使用适合您平台或 IDE 的安装程序。

最新版本 Groovy 3 的 Windows 安装程序可在bintray上找到。(社区工件)

对于 Linux 用户,最新版本的 Groovy 3 也可在Snap Store中找到。(社区工件)

对于 Eclipse 用户,最新版本的 Groovy 3 groovy-eclipse-batch 插件可在bintray上找到。(社区工件)

对于 Intellij 用户,最新社区版本的IDEA支持 Groovy 3。