U-Groovy

发布: (2026年1月1日 GMT+8 23:58)
9 min read
原文: Dev.to

Sure! I see the source link, but I’ll need the actual text you’d like translated. Could you please paste the content you want me to translate into Simplified Chinese? Once I have it, I’ll keep the source line unchanged and preserve all formatting as you requested.

Source:

概述

我已经在 Jenkins 流水线中使用 Groovy 工作了很长一段时间。
现在我正更多地转向 Java 领域,所以这是对这门语言能做什么进行简短总结的好时机。

在我看来,Groovy 是一种 U‑语言未知、被低估且不被注意。很多人听说过它,但只有少数人真正了解它的便利性和通用性。

总体而言,Groovy 类似于 Java——它运行在 JVM 上并且能够理解纯 Java 代码,这让 Java 开发者可以立即上手使用。
与 Java 不同的是,Groovy 可以几乎在任何编程范式中使用,从而实现代码库的高度灵活结构。下面是几个简单的示例,展示了这种灵活性。

A. 命令式编程

命令式编程描述了程序 如何 逐步执行。它是编写计算机程序最常见的方式。

1. 脚本语言

Groovy 可以像 Bash 那样用作普通脚本语言:

println "this is just plain scripting"
a = 1
b = 2
println a + b

2. 动态类型的面向对象编程

你可以在面向对象风格下使用动态类型:

class Car {
    def regNumber = "123AB"
    def numberOfStarts = 0
    def weight = 2

    def start() {
        numberOfStarts++
        println "running"
    }

    def stop() {
        println "stopping"
    }

    def getRegNumber() {
        return regNumber
    }

    // Example of a dynamic getter (not required for the demo)
    def getNumberOfStarts() {
        return numberOfStarts
    }
}

def car = new Car()
car.start()
car.start()
assert car.getNumberOfStarts() == 2
println car.getRegNumber()

car = "this is car"
println car

当静态类型显得大材小用时,你可以简化代码并避免使用它。
动态类型还意味着你可以将不同类型的值赋给同一个变量(def car)。方法可以根据运行时条件返回不同的类型。

3. 静态类型的面向对象编程

如果你更喜欢类似 Java 的风格,可以使用静态类型(注意这里没有分号):

class Plane {
    String regNumber = "123AB"
    int weight = 20
    int numberOfFlights

    void fly() {
        numberOfFlights++
        println "flying"
    }

    void land() {
        println "landing"
    }

    String getRegNumber() {
        return regNumber
    }

    int getNumberOfFlights() {
        return numberOfFlights
    }
}

Plane plane = new Plane()
plane.fly()
plane.fly()
assert plane.getNumberOfFlights() == 2
println plane.getRegNumber()

Source:

B. 声明式编程

声明式编程描述应得到的结果是什么,而不是如何实现它。

1. 基于 Groovy 的声明式语法

你可以在 Groovy 中创建自定义的声明式 DSL(领域特定语言):

println "declarative programming"

def surroundedWithDashes(closure) {
    println "-------------"
    closure.call()
    println "-------------"
}

def show(closure) {
    println closure.call()
}

def sumOfNumbers = { a, b -> a + b }

surroundedWithDashes {
    show { sumOfNumbers(3, 6) }
    show { sumOfNumbers(1, 2) }
    show { sumOfNumbers(5, 10) }
}

在 DSL 定义完成后,调用 surroundedWithDashes { … } 会自动生成所需的布局。

2. 函数式编程

Groovy 支持函数式编程概念,如 纯函数函数链式调用记忆化(memoization)和 柯里化(currying)。大多数任务只需使用三种方法即可表达:collectfindAllinject

println "functional programming"

def data = [1, 5, 8, 3, 5]
def result = data
        .collect { it + 10 }          // 为每个元素加 10
        .findAll { it > 13 }          // 只保留大于 13 的值
        .toUnique()                   // 去除重复项

assert result == [15, 18]

优势不仅体现在链式调用,还体现在函数式特性上,例如:

  • 记忆化 – 为给定输入缓存函数的结果。
  • 柯里化 – 通过固定通用函数的部分参数来创建新函数。

Source:

C. 元编程

元编程可以说是 Groovy 的标志性特性。它允许你在 编译时运行时 修改或生成代码。

1. 编译时元编程

你可以使用 AST(抽象语法树)转换在编译期间生成代码:

println "compile-time metaprogramming"

@Log
class Car2 {
    def regNumber
    def numberOfStarts = 0
    def weight = 2

    @NullCheck
    Car2(regNumber) {
        this.regNumber = regNumber
    }

    def start() {
        numberOfStarts++
        log.info "log: running"
    }

    def stop() {
        log.info "log: stopping"
    }

    String getRegNumber() {
        return regNumber
    }
}

try {
    new Car2().start()
} catch (error) {
    println "error was caught: ${error}"
}

new Car2("12345").start()

@Log 注入日志记录器,而 @NullCheck 为构造函数添加空值检查。源代码中不需要显式的空值检查代码。

2. 运行时元编程

在运行时,你可以拦截、注入,甚至合成方法和属性。这一能力使 Groovy 在构建 DSL、在测试中模拟对象或即时适配现有 API 时极其灵活。

示例

@Log
class Car3 {

    def regNumber
    def numberOfStarts = 0
    def weight = 2

    @NullCheck
    Car3(regNumber) {
        this.regNumber = regNumber
    }

    def start() {
        numberOfStarts++
        log.info "log: running"
    }

    def stop() {
        log.info "log: stopping"
    }

    def getRegNumber() {
        return regNumber
    }

    def methodMissing(String name, args) {
        log.info "log: called missing method: ${name} with arguments: ${args}"
    }

}

def car3 = new Car3("12345")
car3.someUnexistingMethod("with parameter")
car3.metaClass.additionalMethod = { param ->
    log.info "log: called added method: additionalMethod with parameter: ${param}"
}
car3.additionalMethod("paramValue")

在实现了内置方法 methodMissing 后,每当调用类的未定义方法时,都会执行该方法。
additionalMethod 可以在运行时添加到类中,并成功使用。

为什么要使用此特性?

它在以下场景非常有用:

  • 测试 – 尤其是需要 mock 时。
    参见我的相关文章: Mocking with Groovy

  • 高级任务 – 如编写测试框架。
    示例: GitHub 上的简化版 Jenkins 测试框架 – Jenkinson

要点

  • Groovy 在 JVM 上运行,并且能够理解纯 Java 代码。
  • 它支持 命令式、声明式、函数式和元编程 风格。
  • 你可以选择 动态或静态类型(或两者混合)。
  • 它的 DSL 构建AST 转换 能力让你可以根据自己的领域定制语言。

无论你是在为 Jenkins 流水线编写快速脚本,还是在构建大型、类型安全的应用程序,Groovy 都提供了一个许多 Java 开发者忽视的多功能工具箱。祝编码愉快!

摘要

这些是编写 Groovy 代码的众多方式中的简单示例。我只触及了皮毛,但希望这些示例能激发对该语言的好奇心和兴趣。

我自己仍在学习 Groovy,但在一定程度上已经使用了这里展示的所有特性。完整的文档请参阅:

  • Groovy 文档:

如果您还没有浏览过文档,强烈建议阅读——其中还有大量额外的语言特性。

在我看来,这种灵活性是选择 Groovy 作为任务或项目语言时最大的优势,同时它也是一个很好的教学工具。

希望这篇简短的文章对您有帮助。

Back to Blog

相关文章

阅读更多 »

Java简介

人类编写的 Java 代码是如何被机器执行的?它使用一个翻译系统 JDK → JRE → JVM → JIT 将编程语言转换为机器……

Ruby 文件处理和系统命令

打开并读取文件 ```ruby File.open 'p014constructs.rb', 'r' do |f1| while line = f1.gets puts line end end ``` 写入文件 ```ruby File.open 'test.rb', 'w' do |f2| f2.puts 'Created by...' end ```