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,构建脚本可以使用该 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)
    }
}