skip to content

How does Gradle treat a `@Nested` collection (e.g. a `List` of bean specs) for up-to-date checks, and why does element identity matter?

level: seniorimportance: nice to knowfreq 22%

answer

  1. @Nested recurses per element
  2. list -> index identity (reorder = change)
  3. Named -> name identity (order-independent)
  4. affects cache key / hit rate
  5. each element must be annotated

basics

~20 s

Gradle fingerprints every element of a @Nested collection by reading each element's input annotations. Element identity (list index, or Named name) determines how changes — including reordering — affect the task's fingerprint and cache key.

solid answer

~40 s

When `@Nested` is on an `Iterable`/`List`/`Map`/array of beans, Gradle recurses into each element and folds its `@Input`/output properties into the owning task's fingerprint. For ordinary lists, identity is the index, so reordering elements changes the fingerprint and marks the task out-of-date. For `Named` beans (or map entries) identity is the name/key, so the same logical element is tracked across reorders, and renaming counts as a change. This lets you model a list of nested configuration specs (say, multiple output targets) and still get correct incremental builds and cache keys. The cost is that adding, removing, reordering, or editing any element invalidates the task, so keep collections stable and meaningful.

code

kotlin · 10 lines
kotlin
abstract class TargetSpec(private val name: String) : Named {
    override fun getName() = name
    @get:Input abstract val arch: Property<String>
}

abstract class BuildTask : DefaultTask() {
    @get:Nested abstract val targets: ListProperty<TargetSpec>
}
// Named identity: reordering targets does NOT change the fingerprint;
// renaming or editing arch does.

go deeper

for a junior

Know @Nested can apply to a collection and that each element's inputs are tracked.

for a middle

Explain index-based vs Named identity at a basic level and that edits invalidate the task.

for a senior

Reason about cache-hit implications of reorder sensitivity and choose Named/Map or deterministic ordering accordingly.

for a principal

Set conventions for modelling repeated nested specs so org-wide caches stay stable and high-hit despite nondeterministic ordering.

## Recap: @Nested `@Nested` tells Gradle to look *inside* a structured property and pick up its `@Input`, `@InputFiles`, `@OutputFile`, etc. members for fingerprinting. On a single bean that is straightforward; on a collection Gradle applies it to each element. ## Collection fingerprinting For a `@Nested List<TargetSpec>`, Gradle iterates the list, and for each element reads that element's annotated properties, producing a per-element fingerprint. These combine into the task's overall input fingerprint and therefore its build-cache key. ## Identity: index vs Named Gradle needs a stable *identity* per element so it can detect what changed: - **Plain list/array** -> identity is the **position/index**. So `[a, b]` and `[b, a]` fingerprint differently — reordering invalidates the task even if the set of elements is unchanged. - **`Named` beans / Map entries** -> identity is the **name/key**. Element `target(name="linux")` keeps the same identity regardless of position; renaming it to `"linux64"` is a change. This matches how `NamedDomainObjectContainer` elements work. ```kotlin abstract class TargetSpec(private val name: String) : Named { override fun getName() = name @get:Input abstract val arch: Property<String> } abstract class BuildTask : DefaultTask() { @get:Nested abstract val targets: ListProperty<TargetSpec> } ``` ## Why it matters in practice 1. **Correctness** — editing any element's input correctly invalidates the task; you don't get stale outputs. 2. **Cache hits** — two builds with the same logical configuration must produce the same fingerprint. Using `Named` identity makes order irrelevant, improving cache hit rate when element order isn't semantically meaningful. 3. **Surprises** — with plain lists, an incidental reorder (e.g. nondeterministic iteration) silently busts the cache. Prefer `Named` beans or sort deterministically when order is not significant. ## Validation and gotchas Each element type must itself have properly annotated inputs/outputs, or plugin validation warns. Mixing annotated and unannotated fields in element beans causes the same validation issues as top-level tasks. Keep element beans small and fully annotated.

  • Why might a plain List<bean> hurt build-cache hit rate?
    Identity is index-based, so any reorder — even an incidental nondeterministic one — changes the fingerprint and the cache key, causing a miss despite identical logical configuration.
  • How do you make collection order irrelevant to the fingerprint?
    Use `Named` beans (or a Map) so identity is the name/key, not the index; alternatively sort the collection deterministically before it is fingerprinted.

saying these in an interview costs you the question

  • Claiming reordering a plain @Nested list never affects up-to-date checks — it does, because identity is index-based.
  • Forgetting that each element bean still needs its own input/output annotations.

context