How do you wire collection-typed and file-system extension values (e.g. `ListProperty<String>`, `DirectoryProperty`) into a task, and what wiring methods differ from a plain `Property`?
answer
- all are Providers — set() is uniform
- ListProperty: add/addAll/empty
- MapProperty: put/putAll
- DirectoryProperty.file()/.dir() lazy
- avoid .get().asFile in plugin code
basics
~10 sWire them the same lazy way with set: task.tags.set(extension.tags) for ListProperty, task.outDir.set(extension.outDir) for DirectoryProperty. Collections also offer add/addAll to append lazily instead of replacing.
solid answer
~30 sAll of Gradle's lazy property types are providers, so the core wiring is identical: `task.prop.set(extension.prop)`, passing the provider untouched. The differences are in the *extra* lazy mutators. `ListProperty`/`SetProperty` add `add(value)`, `add(provider)`, `addAll(...)` and `empty()` — so a plugin can append to whatever the user configured without resolving it: `task.tags.set(extension.tags); task.tags.add("plugin-default")`. `MapProperty` adds `put`/`putAll`. `DirectoryProperty`/`RegularFileProperty` are providers of `Directory`/`RegularFile` and support relative resolution via `dir(provider)` / `file(provider)`, all lazy. The wiring rule never changes: pass providers, don't `get()`. For file properties, prefer wiring the `DirectoryProperty` itself rather than resolving to a `java.io.File`, so the location stays relocatable and configuration-cache friendly.
code
kotlin · 11 linesabstract class Ext {
abstract val tags: ListProperty<String>
abstract val outDir: DirectoryProperty
}
tasks.register("pkg", PkgTask::class.java) {
it.tags.set(ext.tags) // lazy wire collection
it.tags.add("built-by-plugin") // append without resolving
it.outDir.set(ext.outDir) // lazy wire directory
it.report.set(ext.outDir.file("report.txt")) // derive lazily
}go deeper
Know that set(provider) works for all these types too.
Distinguish set (replace) from add/addAll/put (lazy contribute) and wire file properties as providers.
Reason about preserving user contributions, relocatable file locations, and configuration-cache safety.
Set conventions for plugin authors so collection/file wiring stays lazy and composable across the ecosystem.
## One family, many shapes Gradle's lazy types all implement `Provider<T>` and the writable ones extend `Property`/`HasConfigurableValue`: - `Property<T>` — single value. - `ListProperty<T>`, `SetProperty<T>` — ordered/unique collections. - `MapProperty<K,V>` — key/value map. - `DirectoryProperty`, `RegularFileProperty` — file-system locations (providers of `Directory`/`RegularFile`). Because they're all providers, the **base wiring is uniform**: `task.x.set(extension.x)`. ## Extra lazy mutators for collections Unlike a plain `Property` (which only has `set`/`convention`), collection properties let you *contribute* lazily without clobbering the user's value: ```kotlin // keep the user's list, then append task.tags.set(extension.tags) task.tags.add("plugin-tag") // lazy single add task.tags.addAll(otherProvider) // lazy bulk add ``` `add`/`addAll` accept raw values *or* providers, and they're evaluated at resolution time, so ordering of contributions is preserved and late changes flow through. `MapProperty` mirrors this with `put`/`putAll`. `empty()` resets to an empty (but configured) collection — distinct from leaving it unset. ## File-system properties `DirectoryProperty` and `RegularFileProperty` carry a *location* relative to a project layout, not a bare `File`. Wire them as providers and derive child paths lazily: ```kotlin task.outDir.set(extension.outDir) task.reportFile.set(extension.outDir.file("report.txt")) // Provider<RegularFile> ``` Resolving to `java.io.File` with `.get().asFile` during configuration breaks laziness and relocatability — avoid it in plugin code. ## The invariant No matter the shape, the rule holds: **pass providers through, never `get()` in plugin code.** The collection/file types just give you richer *lazy* ways to combine values before they're resolved.
- How do you let a plugin add a default tag while still honoring the user's list?Wire `set(extension.tags)` then call `task.tags.add("default")` — `add` contributes lazily on top of the wired value rather than replacing it.
- Why prefer wiring a `DirectoryProperty` over resolving to a `File`?It stays lazy and relocatable, so the location can still change and the build remains configuration-cache compatible; `.get().asFile` snapshots a concrete path too early.
saying these in an interview costs you the question
- Thinking collections need a fundamentally different wiring mechanism than `set`.
- Calling `.get().asFile` on a `DirectoryProperty` during configuration.