Explain the difference between convention(), set()/value(), and empty() on a collection property, especially how they interact with add/addAll.
answer
- convention = fallback until explicit set
- set/value = replace whole collection
- empty() = explicit empty, overrides convention
- add appends to explicit base, not to convention
- extend-defaults => seed explicitly then add
basics
~20 sconvention() sets a fallback used only until something is explicitly set. set()/value() replace the whole collection. empty() explicitly resets the property to an empty collection. add/addAll then append on top of whichever base is active.
solid answer
~50 sThese three control the *base* a collection property accumulates onto. `convention(default)` provides a fallback value used only while no explicit value is set — the moment you `set(...)`, `value(...)`, `empty()`, or `add(...)` the property, the convention is overridden. `set(coll)` / `value(coll)` assign the whole collection explicitly, discarding the convention. `empty()` explicitly assigns an *empty* collection, which both clears any convention and resets prior accumulation. The subtle part: `add`/`addAll` append onto the *current* base. If only a convention is present, the first `add` typically overrides the convention rather than appending to it (convention is a fallback, not a starting accumulator) — so to extend defaults you set them with `convention` and then deliberately re-add, or you start from `empty()` and add everything. Knowing this prevents the classic 'my add() wiped the defaults' or 'defaults leaked in unexpectedly' bugs.
code
kotlin · 12 linesval flags = objects.listProperty(String::class.java)
flags.convention(listOf("a"))
// get() == [a] (convention only)
flags.empty()
flags.add("b")
// get() == [b] (empty() overrode the convention, add built from [])
flags.value(listOf("x"))
flags.add("y")
// get() == [x, y] (explicit base then append)go deeper
Know convention = default, set/value = replace, empty = reset to empty.
Explain that convention is a fallback (not an accumulation seed) and how that interacts with add/addAll.
Articulate the two failure modes (defaults lost / defaults leaked) and the overridable-vs-extendable defaults patterns.
Define plugin conventions for default semantics so downstream consumers have predictable override/extend behavior across the org.
## The three operations ### `convention(default)` A **fallback**: the property's value if nobody sets it explicitly. It is *not* part of an accumulation base. As soon as the property is given an explicit value — via `set`, `value`, `empty`, or accumulation that establishes an explicit value — the convention no longer applies. Conventions are how plugins ship sensible defaults that consumers can fully override. ```kotlin val tags = objects.listProperty(String::class.java) tags.convention(listOf("default")) // no explicit value yet -> get() == [default] ``` ### `set(collection)` / `value(collection)` Replace the entire value explicitly. `value(...)` is the fluent variant that returns the property. Both **discard** any convention and any prior accumulation. ```kotlin tags.set(listOf("a", "b")) // get() == [a, b]; default is gone ``` ### `empty()` Explicitly sets the property to an **empty** collection. Two uses: (1) reset accumulation to a clean base, and (2) override a convention with 'definitely nothing'. After `empty()`, `add` builds up from `[]`. ```kotlin tags.convention(listOf("default")) tags.empty() // explicit empty now overrides the convention tags.add("only") // get() == [only] ``` ## How accumulation interacts with the base `add`/`addAll`/`put` append to the **explicit** value. The important nuance: a *convention* is a fallback, not an explicit base, so it does not automatically become the seed that `add` appends to. In practice the robust patterns are: - **Plugin default that the user fully owns:** `convention(defaults)` and document that any explicit `add`/`set` replaces it. - **Always-extend defaults:** start from an explicit base — e.g. `value(defaults)` (or `empty()` then `addAll(defaults)`) — then `add` more; now appends are predictable. ```kotlin // Predictable 'extend the defaults' pattern tags.value(listOf("core")) // explicit base tags.add("extra") // get() == [core, extra] ``` ## Why this matters Confusing convention with an accumulation seed leads to two opposite bugs: 1. **Defaults silently lost** — author relies on `convention`, a consumer calls `add`, and the convention vanishes. 2. **Defaults unexpectedly present** — author wanted a clean slate but never called `empty()`, so a convention leaks into the result. Use `convention` for *overridable* defaults, an explicit `value`/`addAll` base for *extendable* defaults, and `empty()` to force a clean or definitely-empty state.
- A plugin sets convention(listOf("default")). A consumer calls add("extra"). Is the result reliably [default, extra]?No — convention is a fallback, not an accumulation seed, so relying on add to extend it is fragile. To guarantee extension, seed an explicit base (value/addAll) and document it, or have consumers manage it deliberately.
- When would you call empty()?To reset accumulated elements to a clean base, or to explicitly override a convention with 'definitely nothing' so no default leaks through.
- Difference between set() and value()?Both assign the whole collection explicitly and discard the convention; value() is the fluent form that returns the property for chaining.
saying these in an interview costs you the question
- Claiming add always appends on top of the convention.
- Saying convention and set are interchangeable (convention is overridable; set is an explicit assignment).
- Forgetting empty() exists and instead set(emptyList())-ing everywhere without understanding the reset semantics.