Groovy 3 亮点

作者: Paul King

发布时间:2020-02-13 02:28AM


一般改进

Groovy 既有动态特性(支持类似于 Ruby 和 Python 的代码风格)也有静态特性(支持类似于 Java、Kotlin 和 Scala 的风格)。Groovy 持续改进这两种特性——填补任何功能空白。举一个例子,Groovy 提供了许多设施来更好地管理空值。你可以使用 Groovy 的空安全导航运算符,利用 Java 的 Optional,或者为类型检查器提供一个空检查扩展。Groovy 3 通过数组、列表和映射的空安全索引以及一个新的 AST 转换 @NullCheck 增强了这些功能,用于自动对代码进行空检查。

总的来说,语言设计大量借鉴了 Java,因此会密切关注 Java 的变化并酌情采取相应行动。为了使 Groovy 为 Java 模块做好准备并使其与 JDK 9-15 版本良好配合,已经做了大量工作。其他工作显著提高了字节码生成的性能,该生成利用了 JVM 的 invoke dynamic 功能。在 Groovy 4 中,这些领域将进一步改进,目前正在进行额外的更改。

幕后还有许多其他性能改进。编译期间会进行更高效的类型解析,并且针对许多场景生成更高效的字节码。Maven BOM 的添加允许其他项目更灵活地使用 Groovy。

Groovy 在脚本、测试、编写领域特定语言 (DSL) 以及金融计算和数据科学等领域也具有独特的优势。我们一直在努力确保这些优势得以保持。高精度数字的准确性得到了提高并且可配置。Groovy Console 和 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 的 traits)

    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 安装程序(社区工件)可从以下地址获取:https://groovy.jfrog.io/artifactory/dist-release-local/groovy-windows-installer/

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

对于 Eclipse 用户,最新版本的 Groovy 3 groovy-eclipse-batch 插件可从 bintray 获取(更新:现已在 Maven Central 上)。(社区工件)

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