What does the `@Nested` annotation do in Gradle, and where do you apply it?
answer
- recurse into bean's input annotations
- folds nested props into task fingerprint
- works on List/Map/Iterable of beans
- Named elements keyed by name
- alternative to @Internal for structured props
basics
~10 s@Nested marks a getter whose returned object holds further input/output-annotated properties. Gradle walks into that object and treats its annotated members as part of the task's inputs for up-to-date and caching checks.
solid answer
~40 s`@Nested` is an input annotation you put on a task property (getter) whose type is a *bean* containing its own `@Input`, `@InputFiles`, `@OutputFile`, etc. members. It tells Gradle: don't treat this object as opaque — recurse into it and include the nested annotated properties when computing the task's input/output fingerprint for up-to-date checks and the build cache key. This lets you model a complex configuration as a typed nested object (often a nested extension/spec) and still get correct incremental-build behavior. For collections, `@Nested` on a `List`/`Map`/`Iterable` of beans fingerprints each element. Without `@Nested`, Gradle would either ignore the object or fail validation, and changes inside it wouldn't invalidate the task.
code
kotlin · 11 linesabstract class CompressionSpec {
@get:Input abstract val algorithm: Property<String>
@get:Input abstract val level: Property<Int>
}
abstract class PackageTask : DefaultTask() {
@get:Nested abstract val compression: CompressionSpec
@get:OutputFile abstract val archive: RegularFileProperty
@TaskAction fun pack() { /* read compression.algorithm.get() ... */ }
}go deeper
Know that @Nested lets Gradle look inside a structured property to find more inputs.
Apply it to getters of bean types whose fields carry @Input/@OutputFile so incremental build stays correct.
Reason about nested collections, Named identity, and how @Nested ties a DSL spec into the task's cache key.
Set standards so every plugin's structured task inputs are correctly annotated and validated in CI to avoid silent staleness across the org.
## The problem @Nested solves Gradle decides whether a task is up-to-date by fingerprinting its declared **inputs** and **outputs**. Properties are declared with annotations like `@Input`, `@InputFile`, `@InputFiles`, `@OutputFile`, `@OutputDirectory`. But sometimes a task's configuration is naturally a *structured object* — for example a nested extension/spec with several fields. If you only declared the object as a whole, Gradle wouldn't know how to fingerprint it. ## What @Nested does `@org.gradle.api.tasks.Nested` is placed on the getter returning that structured object. It instructs Gradle to **recurse** into the object and read *its* input/output annotations, folding them into the owning task's fingerprint. Effectively the nested bean's annotated properties become part of the task's inputs/outputs. ```kotlin abstract class CompressionSpec { @get:Input abstract val algorithm: Property<String> @get:Input abstract val level: Property<Int> } abstract class PackageTask : DefaultTask() { @get:Nested abstract val compression: CompressionSpec @get:OutputFile abstract val archive: RegularFileProperty } ``` Now changing `compression.level` correctly marks the task out-of-date and changes the build cache key. ## Nested collections `@Nested` also works on `Iterable`, `List`, `Map`, or arrays of beans. Gradle fingerprints each element, using a stable identity (list index, or for `Named` objects their name) so reordering or renaming is detected. ## Relationship to nested extensions This is the connective tissue between the **DSL** side (a nested extension users configure as a block) and the **task** side (the task consuming those values). You declare a nested spec type for the DSL, then either reuse it or mirror it on the task with `@Nested` so the configured values participate in incremental build and caching. ## Validation Gradle's input/output validation (run during `validatePlugins`/at execution) will warn if a structured property is neither annotated nor marked `@Internal`. `@Nested` is the correct choice when the object carries further annotated inputs; `@Internal` is for state that must be ignored.
- What happens to up-to-date checks if you forget @Nested on a structured input property?Gradle won't recurse into the object, so changes to its inner fields won't invalidate the task — leading to stale outputs. Plugin validation may also emit a warning that the property has no input/output annotation.
- How does @Nested behave on a list of beans?Gradle fingerprints each element. For ordinary lists it uses index-based identity; for `Named` beans it uses the name, so reordering or renaming is detected correctly.
saying these in an interview costs you the question
- Claiming @Nested is a DSL annotation that creates the configuration block (it does not — it is an input-fingerprinting annotation).
- Saying @Nested and @Internal are interchangeable; @Internal tells Gradle to ignore the object entirely.