2、Groovy基础速通:Groovy语法特性、闭包、集合操作、与Java的互操作性
说实话,很多Java开发者第一次看到Groovy代码时,第一反应是「这玩意儿也是JVM语言?」。嗯,我当年也一样。但当你真正用上它,尤其是在Gradle构建脚本里写配置时,你会发现——Groovy就是Java的「语法糖PLUS版」,它让你少写很多模板代码,多关注业务逻辑本身。
这一节,我们快速过一遍Groovy的核心特性。别担心,你不需要成为Groovy专家,只需要能看懂Gradle脚本、能自己写点DSL就够了。
2.1 Groovy语法特性:少写代码,多做事
Groovy的语法设计哲学很简单——能省则省。我刚开始用的时候,最不习惯的就是省略分号和括号。但用了一周后,回看Java代码反而觉得「怎么这么啰嗦」。
2.1.1 可选类型与def关键字
在Java里,你写变量必须声明类型:
String name = "张三";
int age = 25;
在Groovy里,你可以用def关键字,让编译器自己去推断类型:
def name = "张三"
def age = 25
我个人习惯在Gradle脚本里大量使用def,因为脚本本身不需要太强的类型约束。但注意,如果你在写库代码,建议还是显式声明类型——方便IDE做代码提示。
2.1.2 字符串的三种写法
Groovy的字符串比Java灵活得多。除了单引号和双引号,还有三引号:
def single = '普通字符串'
def double = "支持插值:${name}"
def triple = """多行字符串
第二行
第三行"""
双引号字符串支持插值表达式,用${}嵌入变量或表达式。我在项目中经常用这个来拼接路径或日志信息,比Java的String.format()直观多了。
2.1.3 可选的分号和括号
Groovy允许省略行尾的分号,也允许省略方法调用的括号:
// Java风格
System.out.println("Hello");
// Groovy风格
println "Hello"
嗯,这里要注意:当方法有多个参数时,括号不能省。比如map.put("key", "value"),你写成map.put "key", "value"虽然也能跑,但可读性会下降。我建议在Gradle脚本里,简单调用可以省括号,复杂调用还是加上。
2.2 闭包:Groovy的灵魂
闭包(Closure)是Groovy最核心的特性,也是Gradle DSL的基石。说白了,闭包就是一段可执行的代码块,你可以把它当作参数传递、赋值给变量、或者直接调用。
2.2.1 闭包的基本语法
// 定义一个闭包
def myClosure = { String name ->
println "Hello, ${name}"
}
// 调用闭包
myClosure("张三") // 输出:Hello, 张三
箭头->左边是参数列表,右边是执行体。如果只有一个参数,可以省略->,用隐式的it变量:
def greet = { println "Hello, ${it}" }
greet("李四") // 输出:Hello, 李四
我在项目中遇到过一个问题:闭包里的it很容易被误用。如果你嵌套闭包,内层闭包的it会覆盖外层的。这时候最好显式命名参数。
2.2.2 闭包作为方法参数
这是Gradle里最常见的用法。比如dependencies块就是一个闭包:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
你想想看,这个dependencies方法接收一个闭包,闭包内部调用implementation方法来添加依赖。这就是DSL的魔力。
def声明局部变量,否则所有闭包共享同一个变量值。
2.3 集合操作:简洁而强大
Groovy对Java的集合做了大量增强。我敢说,你用一次Groovy的集合操作,就再也不想回Java了。
2.3.1 列表操作
def list = [1, 2, 3, 4, 5]
// 遍历
list.each { println it }
// 过滤
def evens = list.findAll { it % 2 == 0 } // [2, 4]
// 映射
def doubled = list.collect { it * 2 } // [2, 4, 6, 8, 10]
// 查找
def firstEven = list.find { it % 2 == 0 } // 2
这些方法都接受闭包作为参数,链式调用非常流畅。我在写Gradle脚本时,经常用collect来转换依赖列表或文件列表。
2.3.2 Map操作
def map = [name: "张三", age: 25, city: "北京"]
// 遍历
map.each { key, value ->
println "${key}: ${value}"
}
// 获取值
println map.name // 张三
println map['name'] // 张三
// 添加/修改
map.email = "zhangsan@example.com"
Groovy的Map可以用点号访问属性,这比Java的map.get("name")优雅太多了。但注意,如果key是字符串变量,你得用map["key"]的形式。
| 操作 | Java写法 | Groovy写法 |
|---|---|---|
| 遍历列表 | for (String s : list) {...} |
list.each { ... } |
| 过滤 | list.stream().filter(...).collect(...) |
list.findAll { ... } |
| Map取值 | map.get("key") |
map.key 或 map['key'] |
2.4 与Java的互操作性:无缝衔接
Groovy最大的优势之一就是和Java的互操作性。你可以在Groovy里直接使用Java类,也可以在Java里调用Groovy代码。说白了,Groovy就是JVM生态的一部分。
2.4.1 在Groovy中使用Java类
// 直接使用Java的ArrayList
def list = new ArrayList<String>()
list.add("Hello")
list.add("World")
// 使用Java的SimpleDateFormat
def sdf = new java.text.SimpleDateFormat("yyyy-MM-dd")
println sdf.format(new Date())
没有任何额外配置,直接import就能用。我在Gradle脚本里经常混用Java和Groovy的API,比如用java.nio.file.Files处理文件,用Groovy的eachLine读取内容。
2.4.2 在Java中调用Groovy
这需要引入Groovy的运行时库。但一旦引入,你就可以在Java里创建Groovy对象、调用Groovy方法:
// Java代码
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
// 执行Groovy脚本
GroovyScriptEngine engine = new GroovyScriptEngine(new String[]{"."});
Object result = engine.run("script.groovy", new Binding());
不过说实话,我在实际项目中很少在Java里直接调用Groovy。更常见的场景是:整个项目用Groovy写,但依赖Java的第三方库。比如Gradle本身就是用Groovy写的,但它依赖了大量Java库。
List<String>,在Groovy里可以取出Integer而不报错。这可能导致运行时异常,建议在边界处做好类型检查。
2.5 知识体系总览
下面这张图帮你梳理Groovy的核心知识点。我建议你把它当作「速查地图」,用到什么就回来翻翻。
这张图把Groovy基础分成四大块:语法特性、闭包、集合操作、互操作性。每一块都是Gradle脚本里高频使用的。我个人建议你重点掌握闭包和集合操作,因为Gradle的DSL几乎全靠闭包实现,而集合操作能帮你写出更简洁的构建逻辑。
build.gradle里的代码一行行看过去,遇到不懂的Groovy语法再回来查。实践是最好的学习方式。
公众号:蓝海资料掘金营,微信deep3321