# Collections

version 9.6.1

Gradle provides types for maintaining collections of objects, intended to work well to extends Gradle’s DSLs and provide useful features such as lazy configuration.

## Collection hierarchy

All Gradle collection types extend from `DomainObjectCollection<T>`, which defines the core contract for live collections — including methods like `configureEach()`, `withType()`, `matching()`, `all()`, `whenObjectAdded()`, and `addLater()`.

    DomainObjectCollection<T>
    ├── DomainObjectSet<T>
    │   └── NamedDomainObjectSet<T>
    └── NamedDomainObjectCollection<T>
        ├── NamedDomainObjectSet<T>
        ├── NamedDomainObjectList<T>
        └── NamedDomainObjectContainer<T>
            └── ExtensiblePolymorphicDomainObjectContainer<T>

| `NamedDomainObjectSet` extends both `DomainObjectSet` and `NamedDomainObjectCollection`.

## Available collections

These collection types are used for managing collections of objects, particularly in the context of build scripts and plugins:

1. `DomainObjectSet<T>`: Represents a set of objects of type T. This set does not allow duplicate elements, and you can add, remove, and query objects in the set.

2. `NamedDomainObjectSet<T>`: A specialization of `DomainObjectSet` where each object has a unique name associated with it. This is often used for collections where each element needs to be uniquely identified by a name.

3. `NamedDomainObjectList<T>`: Similar to `NamedDomainObjectSet`, but represents a list of objects where order matters. Each element has a unique name associated with it, and you can access elements by index as well as by name.

4. `NamedDomainObjectContainer<T>`: A container for managing objects of type T, where each object has a unique name. This container provides methods for adding, removing, and querying objects by name.

5. `ExtensiblePolymorphicDomainObjectContainer<T>`: An extension of `NamedDomainObjectContainer` that allows you to define instantiation strategies for different types of objects. This is useful when you have a container that can hold multiple types of objects, and you want to control how each type of object is instantiated.

These types are commonly used in Gradle plugins and build scripts to manage collections of objects, such as tasks, configurations, or custom domain objects.

## `DomainObjectSet`

A `DomainObjectSet` simply holds a set of configurable objects.

Compared to `NamedDomainObjectContainer`, a `DomainObjectSet` doesn’t manage the objects in the collection. They need to be created and added manually.

You can create an instance using the ObjectFactory.domainObjectSet() method:

build.gradle.kts

    abstract class MyPluginExtensionDomainObjectSet {
        // Define a domain object set to hold strings
        abstract val myStrings: DomainObjectSet<String>

        fun myStrings(action: Action<in DomainObjectSet<String>>) = action.execute(myStrings)
    }

    val dos = extensions.create<MyPluginExtensionDomainObjectSet>("dos")

    dos.apply {
        myStrings {
            add("hello")
        }
    }

build.gradle

    abstract class MyPluginExtensionDomainObjectSet {
        // Define a domain object set to hold strings
        abstract DomainObjectSet<String> getMyStrings()

        void myStrings(Action<? super DomainObjectSet<String>> action) {
            action.execute(getMyStrings())
        }
    }

    extensions.create("dos", MyPluginExtensionDomainObjectSet)

    dos {
        myStrings {
            add("hello")
        }
    }

## `NamedDomainObjectSet`

A `NamedDomainObjectSet` holds a set of configurable objects, where each element has a name associated with it.

This is similar to `NamedDomainObjectContainer`, however a `NamedDomainObjectSet` doesn’t manage the objects in the collection. They need to be created and added manually.

You can create an instance using the ObjectFactory.namedDomainObjectSet() method.

build.gradle.kts

    interface Person : Named {

    }

    abstract class MyPluginExtensionNamedDomainObjectSet {
        // Define a named domain object set to hold Person objects
        abstract val people: NamedDomainObjectSet<Person>

        fun people(action: Action<in NamedDomainObjectSet<Person>>) = action.execute(people)
    }

    val ndos = extensions.create<MyPluginExtensionNamedDomainObjectSet>("ndos")

    ndos.apply {
        people {
           add(objects.newInstance<Person>("bobby"))
        }
    }

build.gradle

    interface Person extends Named {

    }

    abstract class MyPluginExtensionNamedDomainObjectSet {
        // Define a named domain object set to hold Person objects
        abstract NamedDomainObjectSet<Person> getPeople()

        void people(Action<? super NamedDomainObjectSet<Person>> action) {
            action.execute(getPeople())
        }
    }
    extensions.create("ndos", MyPluginExtensionNamedDomainObjectSet)

    ndos {
        people {
           add(objects.newInstance(Person, "bobby"))
        }
    }

## `NamedDomainObjectList`

A `NamedDomainObjectList` holds a list of configurable objects, where each element has a name associated with it.

This is similar to `NamedDomainObjectContainer`, however a `NamedDomainObjectList` doesn’t manage the objects in the collection. They need to be created and added manually.

You can create an instance using the ObjectFactory.namedDomainObjectList() method.

build.gradle.kts

    interface Person : Named {

    }

    abstract class MyPluginExtensionNamedDomainObjectList {
        // Define a named domain object container to hold Person objects
        abstract val people: NamedDomainObjectList<Person>

        fun people(action: Action<in NamedDomainObjectList<Person>>) = action.execute(people)
    }

    val ndol = extensions.create<MyPluginExtensionNamedDomainObjectList>("ndol")

    ndol.apply {
        people {
            add(objects.newInstance<Person>("bobby"))
            add(objects.newInstance<Person>("hank"))
        }
    }

build.gradle

    interface Person extends Named {

    }

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

        void people(Action<? super NamedDomainObjectList<Person>> action) {
            action.execute(getPeople())
        }
    }

    extensions.create("ndol", MyPluginExtensionNamedDomainObjectList)

    ndol {
        people {
            add(objects.newInstance(Person, "bobby"))
            add(objects.newInstance(Person, "hank"))
        }
    }

## `NamedDomainObjectContainer`

A `NamedDomainObjectContainer` manages a set of objects, where each element has a name associated with it.

The container takes care of creating and configuring the elements, and provides a DSL that build scripts can use to define and configure elements. It is intended to hold objects which are themselves configurable, for example a set of custom Gradle objects.

Gradle uses `NamedDomainObjectContainer` type extensively throughout the API. For example, the `project.tasks` object used to manage the tasks of a project is a `NamedDomainObjectContainer<Task>`.

You can create a container instance using the ObjectFactory service, which provides the ObjectFactory.domainObjectContainer() method.

You can also create a container instance using a read-only managed property.

build.gradle.kts

    interface Person : Named {

    }

    abstract class MyPluginExtensionNamedDomainObjectContainer {
        // Define a named domain object container to hold Person objects
        abstract val people: NamedDomainObjectContainer<Person>

        fun people(action: Action<in NamedDomainObjectContainer<Person>>) = action.execute(people)
    }

    val ndoc = extensions.create<MyPluginExtensionNamedDomainObjectContainer>("ndoc")

    ndoc.apply {
        people {
            register("bobby")
            register("hank")
            register("peggy")
        }
    }

build.gradle

    interface Person extends Named {

    }

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

        void people(Action<? super NamedDomainObjectContainer<Person>> action) {
            action.execute(getPeople())
        }
    }

    extensions.create("ndoc", MyPluginExtensionNamedDomainObjectContainer)

    ndoc {
        people {
            bobby
            hank
            peggy
        }
    }

In order to use a type with any of the `domainObjectContainer()` methods, it must either

- be a named managed type; or

- expose a property named “name” as the unique, and constant, name for the object. The `domainObjectContainer(Class)` variant of the method creates new instances by calling the constructor of the class that takes a string argument, which is the desired name of the object.

Objects created this way are treated as custom Gradle types, and so can make use of the features discussed in this chapter, for example service injection or managed properties.

See the above link for `domainObjectContainer()` method variants that allow custom instantiation strategies:

    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();
    }

For each container property, Gradle automatically adds a block to the Groovy and Kotlin DSL that you can use to configure the contents of the container:

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")
            }
        }
    }

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')
            }
        }
    }

## `ExtensiblePolymorphicDomainObjectContainer`

An ExtensiblePolymorphicDomainObjectContainer is a `NamedDomainObjectContainer` that allows you to define instantiation strategies for different types of objects.

You can create an instance using the ObjectFactory.polymorphicDomainObjectContainer() method:

build.gradle.kts

    interface Animal : Named {

    }

    interface Dog : Animal {
        val breed: Property<String>
    }

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

        fun animals(action: Action<in ExtensiblePolymorphicDomainObjectContainer<Animal>>) = action.execute(animals)
    }

    val epdoc = extensions.create<MyPluginExtensionExtensiblePolymorphicDomainObjectContainer>("epdoc")

    // Register available types for container
    epdoc.animals.registerBinding(Dog::class, Dog::class)

    epdoc.apply {
        animals {
            register<Dog>("bubba") {
                breed = "basset hound"
            }
        }
    }

build.gradle

    interface Animal extends Named {

    }

    interface Dog extends Animal {
        Property<String> getBreed()
    }

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

        void animals(Action<? super ExtensiblePolymorphicDomainObjectContainer<Animal>> action) {
            action.execute(getAnimals())
        }
    }

    extensions.create("epdoc", MyPluginExtensionExtensiblePolymorphicDomainObjectContainer)

    // Register available types for container
    epdoc.animals.registerBinding(Dog, Dog)

    epdoc {
        animals {
            bubba(Dog) {
                breed = "basset hound"
            }
        }
    }