ZetCode

Groovy 类

最后修改日期:2025 年 3 月 22 日

在本教程中,我们将探讨如何在 Groovy 中使用类。Groovy 是一种基于 Java 的动态、面向对象语言。Groovy 中的类是创建对象的模板(实例),Groovy 通过注解、自动属性和与 Java 相比更简洁的语法提供了灵活性。

Groovy 普通类

在 Groovy 中,class 关键字用于定义类,类是对象的模板。与 Java 不同,没有可见性修饰符的字段会成为具有自动 getter、setter 的属性,如果未提供无参构造函数,则会自动生成一个。没有修饰符的方法默认是 public 的。

RegularClass.groovy
import groovy.transform.Canonical

@Canonical
class User {
    String name
    String occupation
}

def u = new User('John Doe', 'gardener')
println u

println u.getName()
println u.getOccupation()

def u2 = new User('occupation': 'driver', 'name': 'Roger Roe')

println u2.name
println u2.occupation

在此,User 类使用了 @Canonical,这是一个 Groovy 注解,它会添加一个构造函数、toStringequalshashCode。字段 nameoccupation 是属性,会自动生成 getter/setter。我们通过 new User 或 map 风格的构造函数来创建实例,通过点 notation 或 getter 来访问字段。

$ groovy RegularClass.groovy
User(John Doe, gardener)
John Doe
gardener
User(Roger Roe, driver)
Roger Roe
driver
@Canonical
class User {
    String name
    String occupation
}

@Canonical 通过添加样板方法简化了类定义。没有修饰符的字段会成为具有自动访问器的 public 属性。

def u = new User('John Doe', 'gardener')

使用 @Canonical 添加的构造函数创建 User 实例,通过 new 调用。

def u2 = new User('occupation': 'driver', 'name': 'Roger Roe')

使用 Groovy 的 map 风格构造函数,利用 @Canonical 对命名参数的支持,增强了可读性和灵活性。

对象实例创建

Groovy 提供了多种实例化对象的方式,包括直接构造、类型转换和注解,与 Java 相比,对象创建更简洁、更具表现力。

ObjectCreation.groovy
class User {
    String name
    String occupation

    User(String name, String occupation) {
        this.name = name
        this.occupation = occupation
    }

    String toString() {
        "${this.name} is a ${this.occupation}"
    }
}

def u = new User('John Doe', 'gardener')
println u

def u2 = ['Roger Roe', 'driver'] as User
println u2.name
println u2.occupation

User u3 = ['Paul Smith', 'teacher']
println u3

这展示了三种实例化方法:用于直接构造的 new User,使用 as User 进行类型转换,以及 Groovy 的隐式类型转换。toString 方法提供了可读的输出,属性自动处理访问。

$ groovy ObjectCreation.groovy
John Doe is a gardener
Roger Roe
driver
Paul Smith is a teacher
def u2 = ['Roger Roe', 'driver'] as User

将列表转换为 User 实例,匹配构造函数参数,展示了 Groovy 的动态类型和类型转换。

使用 tap 进行对象创建

Groovy 的 tap 方法允许进行流畅的对象初始化,它会执行一个闭包来设置新创建对象的属性。

TapCreation.groovy
class User {
    String name
    String occupation

    String toString() {
        "${this.name} is a ${this.occupation}"
    }
}

def u = new User().tap {
    name = 'John Doe'
    occupation = 'gardener'
}

println u

tap 通过在闭包中设置属性来初始化 u,提供了一种干净、可链式调用的方式来构建对象,利用了 Groovy 的动态特性。

$ groovy TapCreation.groovy
John Doe is a gardener

@TupleConstructor 注解

@TupleConstructor 注解会根据字段生成经典的构造函数,简化类定义,同时保持类似 Java 的结构。

TupleConstructor.groovy
import groovy.transform.TupleConstructor

@TupleConstructor
class User {
    String name
    String occupation
    List<String> favcols

    String toString() {
        "${this.name} is a ${this.occupation}, favourite colours ${favcols}"
    }
}

def u = new User('John Doe', 'gardener', ['red', 'green', 'blue'])
println u

@TupleConstructor 创建了一个按顺序接收所有字段的构造函数,这里是 nameoccupationfavcols。属性会自动生成,toString 自定义输出,增强了 Groovy 相较于 Java 的简洁性。

$ groovy TupleConstructor.groovy
John Doe is a gardener, favourite colours [red, green, blue]

@MapConstructor 注解

@MapConstructor 注解会生成一个基于 map 的构造函数,允许使用命名参数进行初始化,这是 Groovy 灵活性的一个标志。

MapConstructor.groovy
import groovy.transform.MapConstructor

@MapConstructor
class User {
    String name
    String occupation

    String toString() {
        "${this.name} is a ${this.occupation}"
    }
}

def u = new User(name: 'John Doe', occupation: 'gardener')
println u

@MapConstructor 允许使用 new User(name: ..., occupation:...),使用 map 进行初始化。属性会自动生成,使对象创建直观且易于阅读,与 Java 的僵化形成对比。

$ groovy MapConstructor.groovy
John Doe is a gardener

使用 Immutable Classes 的 findAll

Groovy 的 @Immutable 注解创建了不可变类,非常适合数据对象,并与 findAll 结合用于过滤集合。

ImmutableFindAll.groovy
import groovy.transform.Immutable

@Immutable
class Task {
    String title
    boolean done
}

def tasks = [ 
    new Task("Task 1", true), new Task("Task 2", true), 
    new Task("Task 3", false), new Task("Task 4", true), 
    new Task("Task 5", false) 
]

def res = tasks.findAll { it.done == true }
println res

@Immutable 使 Task 不可变,生成一个构造函数并防止字段更改。findAll 过滤 tasks 以查找已完成的任务,返回 [Task(Task 1, true), ...],展示了 Groovy 的集合方法和不可变性以确保安全性。

$ groovy ImmutableFindAll.groovy
[Task(Task 1, true), Task(Task 2, true), Task(Task 4, true)]

Groovy 抽象类

与 Java 一样,Groovy 使用 abstract 关键字支持抽象类,但它添加了动态功能。抽象类定义了未完成的行为,由子类实现,并且不能直接实例化。

AbstractClass.groovy
abstract class Drawing {
    protected int x = 0
    protected int y = 0

    abstract double area()

    String getCoordinates() {
        "x: ${x}, y: ${y}"
    }
}

class Circle extends Drawing {
    private int r

    Circle(int x, int y, int r) {
        this.x = x
        this.y = y
        this.r = r
    }

    @Override
    double area() {
        this.r * this.r * Math.PI
    }

    String toString() {
        "Circle at x: ${x}, y: ${y}, radius: ${r}"
    }
}

def c = new Circle(12, 45, 22)
println c
println "Area of circle: ${c.area()}"
println c.getCoordinates()

Drawing 抽象类将 area() 声明为抽象,要求在 Circle 中实现。Groovy 的动态类型和字符串插值简化了语法,同时保持了 Java 在继承和多态方面的兼容性。

$ groovy AbstractClass.groovy
Circle at x: 12, y: 45, radius: 22
Area of circle: 1520.53084433746
x: 12, y: 45

Groovy 嵌套类

Groovy 支持像 Java 一样的嵌套类,包括静态嵌套类、内部类、局部类和匿名类,但具有 Groovy 的动态特性以实现简洁性。嵌套类可以提高代码组织性和可读性。

NestedClasses.groovy
// Static Nested Class
class Outer {
    static int x = 5

    static class Nested {
        String toString() { "Static nested; x: ${x}" }
    }
}

def sn = new Outer.Nested()
println sn

// Inner Class
class InnerTest {
    int x = 5

    class Inner {
        String toString() { "Inner class; x: ${x}" }
    }
}

def it = new InnerTest()
def inner = it.new Inner()
println inner

Groovy 的静态嵌套类 Nested 访问 Outer 的静态 x,而内部类 Inner 访问 InnerTest 的实例 x。Groovy 通过省略显式访问修饰符来简化语法,但保留了 Java 的结构以实现兼容性。

$ groovy NestedClasses.groovy
Static nested; x: 5
Inner class; x: 5

@InheritConstructors 注解

Groovy 的 @InheritConstructors 注解会自动从超类继承构造函数,从而减少了子类定义的样板代码。

InheritConstructors.groovy
import groovy.transform.InheritConstructors

class Parent {
    String name

    Parent(String name) { this.name = name }
}

@InheritConstructors
class Child extends Parent { }

def c = new Child('John Doe')
println c.name

@InheritConstructors 允许 Child 继承 Parent 的构造函数,创建了一个名为 "John Doe" 的 Child。此注解简化了继承,增强了 Groovy 相较于 Java 手动复制构造函数的生产力。

$ groovy InheritConstructors.groovy
John Doe

来源

Groovy 面向对象文档

本教程探讨了 Groovy 中类的工作方式,重点介绍了其动态特性、注解和对象创建方法,在 Java 的 OOP 基础上增加了灵活性。

作者

我叫 Jan Bodnar,是一名热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有八年以上的经验。

列出 所有 Groovy 教程