ZetCode

Groovy Switch 表达式

最后修改于 2025 年 3 月 20 日

Groovy 中的 Switch 表达式,也称为模式匹配,提供了一种处理多个条件的简洁方式。与传统 switch 不同,它们返回一个值,增强了表达力。本教程通过示例探讨了它们的通用性。

简单 Switch

Switch 表达式使用 `->` 语法根据条件返回值,简化了基本的决策制定。

SimpleSwitch.groovy
def num = 3
def res = switch (num) {
    case 3 -> "three"
    default -> "other"
}
println res

switch (num) 检查 num (3) 与 case。case 3 匹配,返回 "three"。default case 处理不匹配的值。这种紧凑的形式直接返回值,不像基于语句的 switch。

$ groovy SimpleSwitch.groovy
three

匹配字符串字面量

Switch 表达式可以匹配字符串输入,使其成为基于文本的条件的理想选择,例如用户响应。

SwitchStrings.groovy
def res = System.console().readLine 'Capital of Slovakia?: '
def capital = res.capitalize()

def msg = switch (capital) {
    case 'Bratislava' -> 'correct answer'
    default -> 'wrong answer'
}

println msg

readLine 获取用户输入,并将其大写以保持一致。switch (capital) 检查是否为 "Bratislava",返回 "correct answer",否则通过 default 返回 "wrong answer"。这展示了带回退机制的字符串匹配,对于测验或验证很有用。

$ groovy SwitchStrings.groovy
Capital of Slovakia?: bratislava
correct answer

匹配整数

整数匹配允许 switch 表达式处理数字选项,这在菜单驱动的程序中很常见。

SwitchIntegers.groovy
def menu = '''
Select option
1 - start
2 - slow down
3 - accelerate
4 - pause
5 - terminate
'''

println menu

def opt = System.console().readLine ': ' 

def res = switch (opt as Integer) {
    case 1 -> 'start'
    case 2 -> 'slow down'
    case 3 -> 'accelerate'
    case 4 -> 'pause'
    case 5 -> 'terminate'
    default -> 'unknown'
}

println "your option: $res"

显示菜单,然后 opt as Integer 将输入转换为数字。switch 将 1-5 映射到操作,default 用于无效输入。这将返回一个字符串,如 "your option: accelerate",演示了数字模式匹配。

$ groovy SwitchIntegers.groovy
Select option
1 - start
2 - slow down
3 - accelerate
4 - pause
5 - terminate
: 3
your option: accelerate

匹配类型

Switch 表达式可以匹配对象类型,在混合集合中识别值的类。

SwitchTypes.groovy
def data = [1, 2.2, 'falcon', true, [1, 2, 3], 2g]

for (e in data) {
    def res = switch (e) {
        case Integer -> 'integer'
        case String -> 'string'
        case Boolean -> 'boolean'
        case List -> 'list'
        default -> 'other'
    }
    println res
}

data 包含各种类型。switch (e) 检查每个元素的类型,返回一个标签(例如,1 为 "integer",[1, 2, 3] 为 "list")。default 捕获 BigDecimal (2g)。这突显了用于动态数据的基于类型的模式匹配。

$ groovy SwitchTypes.groovy
integer
other
string
boolean
list
other

多个选项

逗号分隔的 case 将多个值分组到一个结果下,从而简化了具有共享结果的条件。

SwitchMultiple.groovy
def grades = ['A', 'B', 'C', 'D', 'E', 'F', 'FX']

for (grade in grades) {
    switch (grade) {
        case 'A' , 'B' , 'C' , 'D' , 'E' , 'F' -> println('passed')
        case 'FX' -> println('failed')
    }
}

grades 列出了成绩。case 'A' , 'B' , ... 对及格成绩 (A-F) 进行分组,打印 "passed",而 'FX' 打印 "failed"。这种逗号语法将多个匹配合并为一个操作,提高了可读性。

$ groovy SwitchMultiple.groovy
passed
passed
passed
passed
passed
passed
failed

默认选项

递归函数中的 default case 提供了一个通配符,演示了 switch 表达式的返回值实用性。

SwitchDefault.groovy
def factorial(n) {
    switch (n) {
        case 0, 1 -> 1
        default -> n * factorial(n - 1)
    }
}

for (i in 0g..5g) {
    def f = factorial(i)
    println("$i $f")
}

factorial 使用 switch 计算阶乘。case 0, 1 返回 1(基本情况),default 递归乘法。循环打印从 0 到 5 的阶乘(例如,5 120)。这表明 switch 在递归中作为表达式。

$ groovy SwitchDefault.groovy
0 1
1 1
2 2
3 6
4 24
5 120

选项内的 Guard

Guard 使用闭包为 case 添加条件,从而超越简单的相等性来细化匹配。

SwitchGuards.groovy
def rnd = new Random()
def ri = rnd.nextInt(-5, 5)

def res = switch (ri) {
    case { ri < 0 } -> "${ri}: negative value"
    case { ri == 0 } -> "${ri}: zero"
    case { ri > 0 } -> "${ri}: positive value"
}

println res

ri 是一个随机整数(-5 到 4)。switch 使用 guard:{ ri < 0 } 用于负数,{ ri == 0 } 用于零,{ ri > 0 } 用于正数。输出各不相同(例如,“-3: negative value”),展示了动态条件匹配。

$ groovy SwitchGuards.groovy
2: positive value  // varies per run

匹配枚举

Enum 与 switch 表达式自然配对,匹配特定常量以实现类型安全逻辑。

SwitchEnums.groovy
enum Day {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

def days = [Day.Monday, Day.Tuesday, Day.Wednesday, Day.Thursday, 
    Day.Friday, Day.Saturday, Day.Sunday]

def res = []
def random = new Random()

(0..3).each {
    res << days[random.nextInt(days.size())]
}

for (e in res) {
    switch (e) {
        case Day.Monday -> println("monday")
        case Day.Tuesday -> println("tuesday")
        case Day.Wednesday -> println("wednesday")
        case Day.Thursday -> println("thursday")
        case Day.Friday -> println("friday")
        case Day.Saturday -> println("saturday")
        case Day.Sunday -> println("sunday")
    }
}

Day enum 定义了工作日。选择四个随机日期,然后 switch 匹配每个日期,打印其名称(例如,“tuesday”)。输出各不相同,但这表明 enum 是 switch 的目标,利用其固定值来实现清晰度。

$ groovy SwitchEnums.groovy
friday
monday
sunday
thursday  // varies per run

匹配 Enum 范围

Switch 表达式支持 enum 范围,将连续值分组以实现简洁的逻辑。

SwitchEnumRanges.groovy
enum Day {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

def isWeekend(Day d) {
    switch (d) {
        case Day.Monday..Day.Friday -> false 
        case Day.Saturday, Day.Sunday -> true
    }
}

def days = [Day.Monday, Day.Tuesday, Day.Wednesday, Day.Thursday, 
    Day.Friday, Day.Saturday, Day.Sunday]

for (e in days) {
    if (isWeekend(e)) {
        println('weekend')
    } else {
        println('weekday')
    }
}

isWeekend 使用 Monday..Friday 用于工作日(false)和 Saturday, Sunday 用于周末(true)。循环测试所有日期,打印五次“weekday”,然后两次“weekend”。这有效地结合了范围和多个匹配。

$ groovy SwitchEnumRanges.groovy
weekday
weekday
weekday
weekday
weekday
weekend
weekend

匹配对象

Switch 表达式可以匹配对象类型,将相关类分组以进行共享处理。

SwitchObjects.groovy
record Cat(String name) {}
record Dog(String name) {}
record Person(String name) {}

def data = [new Cat('Missy'), new Dog('Jasper'), new Dog('Ace'), 
    new Person('Peter'), 'Jupiter']

for (e in data) {
    switch (e) {
        case Cat, Dog -> println("${e} is a pet")
        case Person -> println("${e} is a human")
        default -> println('unknown')
    }
}

CatDogPerson 是记录。case Cat, Dog 对宠物进行分组,case Person 匹配人类,default 捕获其他(例如,“Jupiter”)。这会打印基于类型的消息,显示对象模式匹配。

$ groovy SwitchObjects.groovy
Cat[name=Missy] is a pet
Dog[name=Jasper] is a pet
Dog[name=Ace] is a pet
Person[name=Peter] is a human
unknown

匹配范围

Switch 表达式中的数字范围检查一个值是否在边界内,这对于对数字进行分类很有用。

SwitchRanges.groovy
def rnd = new Random()
def ri = rnd.nextInt(0, 120)

switch (ri) {
    case 1..30 -> println('value is in 1 to 30')
    case 31..60 -> println('value is in 31 to 60')
    case 61..90 -> println('value is in 61 to 90')
    case 91..120 -> println('value is in 91 to 120')
}

ri 是随机的(0-119)。switch 检查范围(例如,1..30),打印匹配范围的消息。输出各不相同(例如,“value is in 61 to 90”),展示了范围如何有效地划分数值。

$ groovy SwitchRanges.groovy
value is in 61 to 90  // varies per run

匹配正则表达式

Switch 表达式可以将字符串与正则表达式模式匹配,根据复杂标准进行过滤。

SwitchRegex.groovy
def words = ['week', 'bitcoin', 'cloud', 'copper', 'raw', 'war', 
    'cup', 'water']

def selected = []

for (word in words) {
    def res = switch (word) {
        case ~/^w.*/ -> word
        case ~/^c.*/ -> word
        default -> 'skip'
    }
    if (res != 'skip') {
        selected.add(res)
    }
}

println selected

~/^w.*/ 匹配以 "w" 开头的单词,~/^c.*/ 匹配以 "c" 开头的单词。返回匹配的单词,其他则为 "skip"。selected 收集匹配项(例如,“week”、“cloud”),显示了正则表达式模式匹配的实际应用。

$ groovy SwitchRegex.groovy
[week, cloud, copper, war, cup, water]

匹配列表(包含)

Switch 表达式可以检查一个值是否在一个列表中,这对于数据结构中的成员资格测试很有用。

SwitchListContains.groovy
def users = [
    ['John', 'Doe', 'gardener'],
    ['Jane', 'Doe', 'teacher'],
    ['Roger', 'Roe', 'driver'],
    ['Martin', 'Molnar', 'programmer'],
    ['Robert', 'Kovac', 'shopkeeper'],
    ['Tomas', 'Novy', 'programmer']
]

def occupation = 'programmer'

for (user in users) {
    switch (occupation) {
        case user -> println("${user[0]} ${user[1]} is a programmer")
        default -> println("${user[0]} ${user[1]} is not a programmer")
    }
}

users 列出了姓名-职业对。case user 检查 "programmer" 是否在 user(列表)中,并相应地打印。这识别了程序员(Martin、Tomas),使用列表包含作为模式。

$ groovy SwitchListContains.groovy
John Doe is not a programmer
Jane Doe is not a programmer
Roger Roe is not a programmer
Martin Molnar is a programmer
Robert Kovac is not a programmer
Tomas Novy is a programmer

匹配列表(属性检查)

Switch 表达式可以测试列表属性,例如检查子列表中的值,从而增强数据查询。

SwitchListProperty.groovy
def users = [
    ['name': 'Paul', 'grades': ['D', 'A', 'B', 'A']],
    ['name': 'Martin', 'grades': ['F', 'B', 'E', 'FX']],
    ['name': 'Lucia', 'grades': ['A', 'A', 'B', 'FX']],
    ['name': 'Jan', 'grades': ['A', 'B', 'B', 'B']]
]

for (user in users) {
    switch ('FX') {
        case user.grades -> println("${user.name} did not pass")
    }
}

users 将姓名映射到成绩。case user.grades 检查 'FX' 是否在 grades 列表中,并为不及格者(Martin、Lucia)打印。这使用 switch 查询子列表内容,并隐式跳过不匹配项。

$ groovy SwitchListProperty.groovy
Martin did not pass
Lucia did not pass

来源

Groovy 文档

本教程通过示例探讨了 Groovy switch 表达式。

作者

我是 Jan Bodnar,一名经验丰富的程序员。自 2007 年以来,我撰写了超过 1400 篇文章和 8 本电子书,教授编程已超过十年。

列出 所有 Groovy 教程