Gradle 提供了用于维护对象集合的类型,这些类型旨在很好地扩展 Gradle 的 DSL 并提供诸如延迟配置之类的有用特性。

可用集合

这些集合类型用于管理对象集合,特别是在构建脚本和插件的上下文中

  1. DomainObjectSet<T>:表示类型 T 的对象集合。此集合不允许重复元素,您可以在其中添加、删除和查询对象。

  2. NamedDomainObjectSet<T>:是 DomainObjectSet 的特化,其中每个对象都关联着一个唯一的名称。这常用于需要通过名称唯一标识每个元素的集合。

  3. NamedDomainObjectList<T>:类似于 NamedDomainObjectSet,但表示一个对象列表,其中顺序很重要。每个元素都关联着一个唯一的名称,您可以通过索引和名称访问元素。

  4. NamedDomainObjectContainer<T>:一个用于管理类型 T 对象的容器,其中每个对象都关联着一个唯一的名称。此容器提供了按名称添加、删除和查询对象的方法。

  5. ExtensiblePolymorphicDomainObjectContainer<T>:是 NamedDomainObjectContainer 的扩展,允许您为不同类型的对象定义实例化策略。当您有一个可以容纳多种类型对象的容器,并且想要控制每种类型对象的实例化方式时,这会很有用。

这些类型常用于 Gradle 插件和构建脚本中,以管理对象集合,例如任务、配置或自定义领域对象。

1. DomainObjectSet

一个 DomainObjectSet 简单地持有一组可配置的对象。

NamedDomainObjectContainer 相比,DomainObjectSet 不管理集合中的对象。它们需要手动创建和添加。

您可以使用 ObjectFactory.domainObjectSet() 方法创建一个实例

build.gradle.kts
abstract class MyPluginExtensionDomainObjectSet {
    // Define a domain object set to hold strings
    val myStrings: DomainObjectSet<String> = project.objects.domainObjectSet(String::class)

    // Add some strings to the domain object set
    fun addString(value: String) {
        myStrings.add(value)
    }
}
build.gradle
abstract class MyPluginExtensionDomainObjectSet {
    // Define a domain object set to hold strings
    DomainObjectSet<String> myStrings = project.objects.domainObjectSet(String)

    // Add some strings to the domain object set
    void addString(String value) {
        myStrings.add(value)
    }
}

2. NamedDomainObjectSet

一个 NamedDomainObjectSet 持有一组可配置的对象,其中每个元素都关联着一个名称。

这类似于 NamedDomainObjectContainer,但 NamedDomainObjectSet 不管理集合中的对象。它们需要手动创建和添加。

您可以使用 ObjectFactory.namedDomainObjectSet() 方法创建一个实例。

build.gradle.kts
abstract class Person(val name: String)

abstract class MyPluginExtensionNamedDomainObjectSet {
    // Define a named domain object set to hold Person objects
    private val people: NamedDomainObjectSet<Person> = project.objects.namedDomainObjectSet(Person::class)

    // Add a person to the set
    fun addPerson(name: String) {
        people.plus(name)
    }
}
build.gradle
abstract class Person {
    String name
}

abstract class MyPluginExtensionNamedDomainObjectSet {
    // Define a named domain object set to hold Person objects
    NamedDomainObjectSet<Person> people = project.objects.namedDomainObjectSet(Person)

    // Add a person to the set
    void addPerson(String name) {
        people.create(name)
    }
}

3. NamedDomainObjectList

一个 NamedDomainObjectList 持有一组可配置的对象列表,其中每个元素都关联着一个名称。

这类似于 NamedDomainObjectContainer,但 NamedDomainObjectList 不管理集合中的对象。它们需要手动创建和添加。

您可以使用 ObjectFactory.namedDomainObjectList() 方法创建一个实例。

build.gradle.kts
abstract class Person(val name: String)

abstract class MyPluginExtensionNamedDomainObjectList {
    // Define a named domain object list to hold Person objects
    private val people: NamedDomainObjectList<Person> = project.objects.namedDomainObjectList(Person::class)

    // Add a person to the container
    fun addPerson(name: String) {
        people.plus(name)
    }
}
build.gradle
abstract class Person {
    String name
}

abstract class MyPluginExtensionNamedDomainObjectList {
    // Define a named domain object container to hold Person objects
    NamedDomainObjectList<Person> people = project.container(Person)

    // Add a person to the container
    void addPerson(String name) {
        people.create(name: name)
    }
}

4. NamedDomainObjectContainer

一个 NamedDomainObjectContainer 管理一组对象,其中每个元素都关联着一个名称。

该容器负责创建和配置元素,并提供构建脚本可用于定义和配置元素的 DSL。它旨在容纳自身可配置的对象,例如一组自定义 Gradle 对象。

Gradle 在整个 API 中广泛使用 NamedDomainObjectContainer 类型。例如,用于管理项目任务的 project.tasks 对象就是一个 NamedDomainObjectContainer<Task>

您可以使用 ObjectFactory 服务创建一个容器实例,该服务提供了 ObjectFactory.domainObjectContainer() 方法。这也可以使用 Project.container() 方法获取,但在自定义 Gradle 类型中,通常最好使用注入的 ObjectFactory 服务,而不是传递 Project 实例。

您也可以使用只读的托管属性创建一个容器实例。

build.gradle.kts
abstract class Person(val name: String)

abstract class MyPluginExtensionNamedDomainObjectContainer {
    // Define a named domain object container to hold Person objects
    private val people: NamedDomainObjectContainer<Person> = project.container(Person::class)

    // Add a person to the container
    fun addPerson(name: String) {
        people.create(name)
    }
}
build.gradle
abstract class Person {
    String name
}

abstract class MyPluginExtensionNamedDomainObjectContainer {
    // Define a named domain object container to hold Person objects
    NamedDomainObjectContainer<Person> people = project.container(Person)

    // Add a person to the container
    void addPerson(String name) {
        people.create(name: name)
    }
}

为了使用任何 domainObjectContainer() 方法,类型必须满足以下条件之一:

  • 命名托管类型;或

  • 暴露一个名为“name”的属性,作为对象的唯一且常量名称。该方法的 domainObjectContainer(Class) 变体通过调用接受字符串参数的类构造函数来创建新实例,该字符串参数即为对象期望的名称。

通过这种方式创建的对象被视为自定义 Gradle 类型,因此可以使用本章讨论的特性,例如服务注入或托管属性。

有关允许自定义实例化策略的 domainObjectContainer() 方法变体,请参阅上面的链接

public interface DownloadExtension {
    NamedDomainObjectContainer<Resource> getResources();
}

public interface Resource {
    // Type must have a read-only 'name' property
    String getName();

    Property<URI> getUri();

    Property<String> getUserName();
}

对于每个容器属性,Gradle 会自动向 Groovy 和 Kotlin DSL 添加一个块,您可以使用该块来配置容器的内容

build.gradle.kts
plugins {
    id("org.gradle.sample.download")
}

download {
    // Can use a block to configure the container contents
    resources {
        register("gradle") {
            uri = uri("https://gradle.org.cn")
        }
    }
}
build.gradle
plugins {
    id("org.gradle.sample.download")
}

download {
    // Can use a block to configure the container contents
    resources {
        register('gradle') {
            uri = uri('https://gradle.org.cn')
        }
    }
}

5. ExtensiblePolymorphicDomainObjectContainer

一个 ExtensiblePolymorphicDomainObjectContainer 是一个 NamedDomainObjectContainer,它允许您为不同类型的对象定义实例化策略。

您可以使用 ObjectFactory.polymorphicDomainObjectContainer() 方法创建一个实例

build.gradle.kts
abstract class Animal(val name: String)

class Dog(name: String, val breed: String) : Animal(name)

abstract class MyPluginExtensionExtensiblePolymorphicDomainObjectContainer(objectFactory: ObjectFactory) {
    // Define a container for animals
    private val animals: ExtensiblePolymorphicDomainObjectContainer<Animal> = objectFactory.polymorphicDomainObjectContainer(Animal::class)

    // Add a dog to the container
    fun addDog(name: String, breed: String) {
        var dog : Dog = Dog(name, breed)
        animals.add(dog)
    }
}
build.gradle
abstract class Animal {
    String name
}

abstract class Dog extends Animal {
    String breed
}

abstract class MyPluginExtensionExtensiblePolymorphicDomainObjectContainer {
    // Define a container for animals
    ExtensiblePolymorphicDomainObjectContainer<Animal> animals

    MyPluginExtensionExtensiblePolymorphicDomainObjectContainer(ObjectFactory objectFactory) {
        // Create the container
        animals = objectFactory.polymorphicDomainObjectContainer(Animal)
    }

    // Add a dog to the container
    void addDog(String name, String breed) {
        animals.create(Dog, name: name, breed: breed)
    }
}