1. 概述

2. 环境配置

在Maven项目中使用Groovy时,需要在pom.xml中添加以下配置:

<build>
    <plugins>
        // ...
        <plugin>
            <groupId>org.codehaus.gmavenplus</groupId>
            <artifactId>gmavenplus-plugin</artifactId>
            <version>1.5</version>
       </plugin>
   </plugins>
</build>
<dependencies>
    // ...
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</artifactId>
        <version>2.4.10</version>
    </dependency>
</dependencies>

最新版Maven插件可查看这里groovy-all最新版可查看这里

3. 核心特性

Groovy提供了许多实用特性。下面我们来看看这门语言的基础构件及其与Java的差异。

3.1. 动态类型

Groovy最重要的特性之一是支持动态类型。

类型定义是可选的,实际类型在运行时确定。看这两个类:

class Duck {
    String getName() {
        'Duck'
    }
}
class Cat {
    String getName() {
        'Cat'
    }
}

这两个类都定义了getName方法,但没有显式声明接口。

现在假设有一个包含鸭子和猫的列表,它们都有getName方法。在Groovy中我们可以这样操作:

Duck duck = new Duck()
Cat cat = new Cat()

def list = [duck, cat]
list.each { obj ->
    println obj.getName()
}

代码能正常编译,输出结果为:

Duck
Cat

3.2. 隐式布尔转换

类似JavaScript,Groovy在需要时会将对象自动转换为布尔值(例如在if语句或取反操作时):

if("hello") {...}
if(15) {...}
if(someObject) {...}

记住以下转换规则:

  • ✅ 非空集合、数组、映射 → true
  • ✅ 至少有一个匹配的Matchertrue
  • ✅ 还有元素的迭代器枚举true
  • ✅ 非空字符串GStringCharSequencetrue
  • ✅ 非零数字 → true
  • ✅ 非空对象引用 → true

*若要自定义隐式布尔转换,可以定义asBoolean()方法。*

3.3. 默认导入

以下包会自动导入,无需显式声明:

import java.lang.* 
import java.util.* 
import java.io.* 
import java.net.* 

import groovy.lang.* 
import groovy.util.* 

import java.math.BigInteger 
import java.math.BigDecimal

4. AST转换

AST(抽象语法树)转换允许我们介入Groovy编译过程,按需定制编译行为。这些转换在编译时完成,运行时没有性能损耗。我们可以创建自定义AST转换,也可以直接使用内置转换。

下面介绍几个值得关注的注解。

*4.1. @TypeChecked注解*

该注解强制编译器对标注代码进行严格类型检查。类型检查机制可扩展,必要时甚至能提供比Java更严格的检查。

看下面这个例子:

class Universe {
    @TypeChecked
    int answer() { "forty two" }
}

尝试编译时会报错:

[Static type checking] - Cannot return value of type java.lang.String on method returning type int

@TypeChecked可应用于类和方法。

*4.2. @CompileStatic注解*

该注解让编译器像Java代码一样执行编译时检查,然后进行静态编译,绕过Groovy元对象协议。

  • 标注类时:类中所有方法、属性、内部类等都会被类型检查
  • 标注方法时:仅对该方法内的闭包和匿名内部类进行静态编译

5. 属性

在Groovy中,我们可以创建POGO(普通Groovy对象),功能类似Java的POJO,但更简洁——编译时会自动为公共属性生成getter/setter。⚠️ 注意:仅当这些方法未显式定义时才会生成。

这让我们既可以用开放字段定义属性,又能在需要时重写取值/赋值行为。

看这个对象:

class Person {
    String name
    String lastName
}

默认类、字段和方法作用域都是public,所以这是公共类,两个字段也是公共的。

编译器会将它们转为私有字段,并添加*getName()setName()getLastName()setLastName()*方法。如果显式定义了某字段的getter/setter,编译器就不会再生成。

5.1. 简化写法

Groovy为属性访问提供了简化语法。不必像Java那样调用getter/setter,可以直接用字段访问语法:

resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME
resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME

resourcePrototype.setName("something")
resourcePrototype.name = "something"

6. 操作符

下面看看Groovy在Java操作符基础上新增的特性。

6.1. 空安全解引用

最常用的是空安全解引用操作符“?”,调用null对象的方法或属性时能避免NullPointerException。在链式调用中特别有用,因为链中任何环节都可能返回null

例如可以安全调用:

String name = person?.organization?.parent?.name

上例中如果personperson.organizationorganization.parentnull,整个表达式返回null

6.2. Elvis操作符

Elvis操作符“?:”可以简化三元表达式。下面两种写法等价:

String name = person.name ?: defaultName

String name = person.name ? person.name : defaultName

两者都在person.name为Groovy真值(非null且长度非零)时将其赋值给name

6.3. 太空船操作符

太空船操作符“<=>”是关系操作符,功能类似Java的*compareTo()*:比较两个对象,根据参数值返回-1、0或+1。

  • 左参数 > 右参数 → 返回1
  • 左参数 < 右参数 → 返回-1
  • 参数相等 → 返回0

最大优势是能优雅处理nullx <=> y永远不会抛出NullPointerException

println 5 <=> null

上例会输出1。

7. 字符串

Groovy提供多种字符串字面量表示法。支持Java的双引号字符串,也允许使用单引号。

多行字符串(其他语言常称为heredoc)使用三引号(单双引号均可):

def multiline = """
    第一行
    第二行
"""

双引号字符串支持使用*${}*语法插值:

def name = "Bill Gates"
def greeting = "Hello, ${name}"

实际上*${}*中可放任意表达式:

def name = "Bill Gates"
def greeting = "Hello, ${name.toUpperCase()}"

包含*${}表达式的双引号字符串称为GString,否则是普通String*对象:

def a = "hello" 
assert a.class.name == 'java.lang.String'

def b = 'hello'
assert b.class.name == 'java.lang.String'

def c = "${b}"
assert c.class.name == 'org.codehaus.groovy.runtime.GStringImpl'

8. 集合与映射

看看Groovy如何处理基础数据结构。

8.1. 列表

Java中创建ArrayList并添加元素:

List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");

Groovy中只需:

List list = ['Hello', 'World']

列表默认是java.util.ArrayList类型,也可显式调用构造函数声明。

没有专门的Set语法,但可以用类型强制转换:

Set greeting = ['Hello', 'World']

def greeting = ['Hello', 'World'] as Set

8.2. 映射

映射语法类似,但需指定键值对(用冒号分隔):

def key = 'Key3'
def aMap = [
    'Key1': 'Value 1', 
    Key2: 'Value 2',  // 注意:Key2会被当作变量名而非字符串
    (key): 'Another value'  // 使用括号强制作为变量求值
]

初始化后会得到包含以下条目的LinkedHashMapKey1 -> Value1, Key2 -> Value 2, Key3 -> Another Value

访问映射条目的多种方式:

println aMap['Key1']
println aMap[key]
println aMap.Key1

9. 控制结构

9.1. 条件语句:if-else

Groovy支持标准的if/else语法:

if (...) {
    // ...
} else if (...) {
    // ...
} else {
    // ...
}

9.2. 条件语句:switch-case

def x = 1.23
def result = ""
switch (x) {
    case "foo": 
        result = "found foo"
        break
    case [4, 5, 6, 'inList']: 
        result = "is in list"
        break
    case 12..30: 
        result = "is in range"
        break
    case Number: 
        result = "is number"
        break
    case { it > 3 }: 
        result = "greater than 3"
        break
    default: 
        result = "default"
}
assert result == "is number"

9.3. 循环:while

Groovy支持标准的while循环:

def x = 0
def y = 5

while ( y-- > 0 ) {
    x++
}

9.4. 循环:for

Groovy推崇这种简洁的for循环结构:

for (variable in iterable) { body }

for循环遍历iterable。常用可迭代对象包括:范围、集合、映射、数组、迭代器和枚举。实际上任何对象都可以是可迭代的。

如果循环体只有一条语句,花括号可省略。以下是遍历范围列表数组映射字符串的示例:

def x = 0
for ( i in 0..9 ) {
    x += i
}

x = 0
for ( i in [0, 1, 2, 3, 4] ) {
    x += i
}

def array = (0..4).toArray()
x = 0
for ( i in array ) {
    x += i
}

def map = ['abc':1, 'def':2, 'xyz':3]
x = 0
for ( e in map ) {
    x += e.value
}

x = 0
for ( v in map.values() ) {
    x += v
}

def text = "abc"
def list = []
for (c in text) {
    list.add(c)
}

对象迭代使Groovy的for循环成为强大的控制结构。它是使用闭包迭代对象方法(如Collection的each方法)的有效替代方案。

主要区别在于:for循环体不是闭包,只是一个代码块:

for (x in 0..9) { println x }

而这是一个闭包:

(0..9).each { println it }

虽然外观相似,但构造方式完全不同。

闭包是独立对象,具有特殊特性。它可以在不同位置创建并传递给each方法。而for循环体直接在其出现位置生成为字节码,没有特殊作用域规则。

10. 异常处理

最大区别是:不强制处理受检异常。

处理通用异常时,可将可能抛出异常的代码放在try/catch块中:

try {
    someActionThatWillThrowAnException()
} catch (e) {  // 注意:这里原文有语法错误,已修正
    // 记录错误消息和/或以某种方式处理
}

不声明捕获的异常类型时,任何异常都会被捕获。

11. 闭包

简单说,闭包是可传递给变量的匿名可执行代码块,能访问其定义上下文中的数据。

类似匿名内部类,但不实现接口或继承基类。也类似Java中的lambda表达式。

有趣的是,Groovy能充分利用为支持lambda而新增的JDK特性,特别是流式API。任何需要lambda表达式的地方都可以使用闭包。

看下面例子:

def helloWorld = {
    println "Hello World"
}

变量helloWorld现在持有闭包引用,可通过调用其call方法执行:

helloWorld.call()

Groovy允许使用更自然的方法调用语法——它会自动调用call方法:

helloWorld()

11.1. 参数

和方法一样,闭包可以有参数。有三种变体:

// 无参数
def noParams = { println "No params" }

// 隐式单参数(默认名为it)
def printTheParam = { println it }

// 显式多参数
def power = { int x, int y ->
    return Math.pow(x, y)
}

调用方式:

noParams()
printTheParam('hello')
printTheParam 'hello'  // Groovy允许省略括号
println power(2, 3)

参数类型定义与变量相同。定义类型后只能传入该类型,但也可省略类型传入任意值:

def say = { what ->
    println what
}
say "Hello World"

11.2. 可选返回

闭包的最后一条语句可隐式返回,无需写return语句。这能最大限度减少样板代码。计算数字平方的闭包可简化为:

def square = { it * it }
println square(4)

此闭包使用了隐式参数it和可选return语句。

12. 总结

本文快速介绍了Groovy语言及其核心特性。我们从基础概念(如基本语法、条件语句和操作符)开始,也演示了一些高级特性(如操作符和闭包)。

若想了解更多语言细节和语义,可直接访问官方文档


原始标题:Introduction to Groovy Language

« 上一篇: sun.misc.Unsafe 指南
» 下一篇: JVM 垃圾收集器详解