0%

Groovy 4之新特性预览

概述

为了进一步兼容Java,我们Groovy核心团队决定从Groovy 4.0.0开始支持Switch Expression、Record Class、Sealed Class等Java语言特性。值得注意的是,即使Groovy 4.0.0运行在Java 8上,这些新特性依然可以使用。除了兼容Java语言特性外,Groovy自身也新增了不少实用的新特性,比如增强Range、支持TOML、Java化(@POJO)、GINQ(Groovy-Integrated Query)等。

新特性预览

Switch Expression

使用->表示各个逻辑分支

1
2
3
4
5
6
7
8
def i = 1
def result = switch(i) {
case 0 -> 'zero'
case 1 -> 'one'
case 2 -> 'two'
default -> throw new IllegalStateException('unknown number')
}
assert 'one' == result

使用花括号{...}处理较为复杂逻辑分支

1
2
3
4
5
6
7
8
9
10
11
12
13
def i = 3
def result = switch(i) {
case 0 -> 'zero'
case 1 -> 'one'
case 2 -> 'two'
case 3 -> {
def p1 = 'th'
def p2 = 'ree'
"${p1}${p2}" // 最后一个表达式的`yield`是可选的
}
default -> throw new IllegalStateException('unknown number')
}
assert 'three' == result

使用yield来产生switch expression的结果值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def lang = 'CN'
def i = 3
def result = switch(i) {
case 0 -> 'zero'
case 1 -> 'one'
case 2 -> 'two'
case 3 -> {
if ('CN' == lang) {
yield '三'
}
yield 'three'
}
default -> throw new IllegalStateException('unknown number')
}
assert '三' == result

使用:表示逻辑分支并使用yield产生switch expression的结果值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def lang = 'EN'
def i = 3
def result = switch(i) {
case 0: yield 'zero'
case 1: yield 'one'
case 2: yield 'two'
case 3:
if ('CN' == lang) {
yield '三'
}
yield 'three'
default: throw new IllegalStateException('unknown number')
}
assert 'three' == result

使用:复用逻辑分支

1
2
3
4
5
6
7
8
9
10
11
def i = 4
def result = switch(i) {
case 0: yield 'zero'
case 1: yield 'one'
case 2: yield 'two'
case 3: yield 'three'
case 4:
case 5:
default: 'unknown number'
}
assert 'unknown number' == result

Record Class

使用record关键字定义Record Class

1
2
3
4
5
6
record Person(String name, int age) {
}

def p = new Person('山风小子', 37)
assert '山风小子' == p.name()
assert 37 == p.age()

定义Compact constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
record Person(String name, int age) {
Person {
if (age < 0) {
throw new IllegalArgumentException("invalid age: ${age}")
}

name = name.toUpperCase()
}
}

def p1 = new Person('Daniel', 37)
assert 'DANIEL' == p1.name()

try {
def p2 = new Person('Unknown', -1)
assert false: 'Should never reach here'
} catch (IllegalArgumentException e) {
assert 'invalid age: -1' == e.message
}

Sealed Class

使用sealed定义Sealed Class,注:如果子类没有显式声明finalsealednon-sealed,那么默认non-sealed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 只允许`Ellipse`、`Rectangle`以及`Triangle`继承`Shape`
sealed abstract class Shape permits Ellipse, Rectangle, Triangle {
}

// `non-sealed`可选,此处作为示例进行显式声明
non-sealed class Ellipse extends Shape {
}
class Circle extends Ellipse {
}

sealed class Rectangle extends Shape permits Square {
}
class Square extends Rectangle {
}

final class Triangle extends Shape {
}

assert new Ellipse() instanceof Shape
assert new Circle() instanceof Shape
assert new Rectangle() instanceof Shape
assert new Square() instanceof Shape
assert new Triangle() instanceof Shape

增强Range

新增<..(左开右闭区间)以及<..<(开区间)

1
2
3
4
assert [2, 3, 4, 5, 6] == 1<..6   // 新增左开右闭区间
assert [2, 3, 4, 5] == 1<..<6 // 新增开区间
assert [1, 2, 3, 4, 5] == 1..<6 // 已有左闭右开区间
assert [1, 2, 3, 4, 5, 6] == 1..6 // 已有闭区间

支持TOML

  • 解析TOML
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def ts = new groovy.toml.TomlSlurper()
    def toml = ts.parseText '''
    # This is a TOML document.
    title = "TOML Example"
    [owner]
    name = "山风小子"
    [blog]
    domainName = "blog.sunlan.me"
    languages = ["CN", "EN"]
    '''

    assert 'TOML Example' == toml.title
    assert '山风小子' == toml.owner.name
    assert 'blog.sunlan.me' == toml.blog.domainName
    assert ['CN', 'EN'] == toml.blog.languages
  • 创建TOML(暂不支持美化输出结果)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def tb = new groovy.toml.TomlBuilder()
    tb {
    title 'TOML Example'
    owner {
    name '山风小子'
    }
    blog {
    domainName 'blog.sunlan.me'
    languages 'CN', 'EN'
    }
    }

    assert '''\
    title = 'TOML Example'
    owner.name = '山风小子'
    blog.domainName = 'blog.sunlan.me'
    blog.languages = ['CN', 'EN']
    ''' == tb.toString()

Java化(@POJO

Groovy编译出来的class依赖Groovy自带的类(比如:GroovyObject),因此在运行Groovy程序时不得不将Groovy运行时jar文件都引入。而@POJO@CompileStatic结合使用便可助我们“轻装上阵”,摆脱Groovy运行时jar文件,但有个大前提,Groovy程序本身没有用到Groovy自带的类,包括GString

1
2
3
4
@groovy.transform.CompileStatic
@groovy.transform.stc.POJO
class HelloGroovy {
}

编译后所生成的bytecode如下所示(注:Groovy的@Generated不影响程序独立运行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// class version 61.0 (61)
// access flags 0x21
public class HelloGroovy {
// access flags 0x1
public <init>()V
@Lgroovy/transform/Generated;()
L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
RETURN
LOCALVARIABLE this LHelloGroovy; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
}

GINQ(Groovy-Integrated Query)

详见《Groovy 4之新特性GINQ预览

其他

详见Groovy 4.0.0 Release Notes

结束语

Groovy 4.0.0除了新增众多语言特性外,还修正了许多bug,尤其是静态编译那块。另外,Groovy 4.0.0也是唯一真正支持Java 17的版本,故一旦发布,强烈建议升级至该版本。

相关文章

《Back in the Groovy 4》