skip to content

What does Provider.map do in Gradle, and why is it preferred over reading a provider's value at configuration time?

level: juniorimportance: must knowfreq 62%

answer

  1. map = lazy transform
  2. skipped when source absent
  3. transformer gets non-null input
  4. carries task dependency
  5. avoid get() at config time

basics

~10 s

map transforms a provider's value into a new provider using a function, without computing it now. The transform runs lazily only when the value is finally queried, so it stays deferred until execution time.

solid answer

~40 s

`Provider<T>.map { ... }` returns a new `Provider<R>` whose value is the result of applying the function to the source provider's value. Crucially, the mapping function is **not** executed when you call `map` — it runs lazily the first time the resulting provider is queried (typically at task-execution time). This is preferred over calling `provider.get()` during configuration because `get()` forces evaluation immediately, which can fail if the value isn't set yet, breaks up-to-date checking, and defeats configuration avoidance. By chaining `map`, you build a deferred pipeline: `versionProvider.map { "app-$it.jar" }` produces a derived value that recomputes only when needed and carries task dependencies along.

code

kotlin · 5 lines
kotlin
val version = objects.property(String::class.java).convention("1.0.0")
val archiveName: Provider<String> = version.map { "app-$it.jar" }
tasks.register("printName") {
    doLast { println(archiveName.get()) } // transform runs here, lazily
}

go deeper

for a junior

Know map transforms a provider lazily and that you shouldn't call get() during configuration.

for a middle

Explain absent-propagation, that the transformer gets a non-null input, and the deferred-evaluation timing.

for a senior

Connect map to configuration avoidance, up-to-date checking, and implicit task-dependency carrying.

for a principal

Frame lazy providers as the mechanism enabling the configuration cache and incremental builds across a large plugin ecosystem.

## The lazy model Gradle's lazy configuration API is built around `Provider<T>` (a read-only deferred value) and `Property<T>` (a `Provider` you can also assign). A `Provider` does not hold a value eagerly — it holds a *recipe* for computing one, evaluated only when `get()`/`getOrNull()`/`getOrElse()` is actually called. This lets Gradle skip work for tasks that don't run and wire values that aren't known yet at configuration time. ## What map does `Provider<T>.map(transformer: T -> R): Provider<R>` creates a **new** provider that, when queried, first resolves the upstream provider, then applies the transform. Key properties: - **Lazy:** the lambda runs only when the downstream provider is queried, not when `map` is called. - **Absent-propagating:** if the source provider has no value (is absent), the map function is skipped entirely and the result is also absent. The transformer is therefore guaranteed a non-null input. - **Dependency-carrying:** if the source provider was produced by a task (e.g. `task.flatMap { it.outputFile }`), the derived provider implicitly carries that task dependency, so wiring it into another task's input establishes the build ordering automatically. ## Why not just call get() Reading `provider.get()` during configuration: 1. Forces evaluation immediately — fails with `Cannot query the value ... because it has no value` if not yet set. 2. Captures a snapshot; later changes to the source are lost. 3. Breaks configuration cache and up-to-date checks because the value is baked into config rather than re-derived. ```kotlin val version = project.objects.property(String::class.java).convention("1.0.0") val jarName: Provider<String> = version.map { "app-$it.jar" } // nothing computed yet; lambda runs only when jarName.get() is called ``` Use `map` whenever you need a *derived* value; reserve `get()` for the latest possible moment (task action), or let Gradle query it for you by wiring providers directly into task input properties.

  • What happens to the map transformer if the source provider is absent?
    It is not invoked at all; the resulting provider is also absent. This guarantees the transformer never receives null.
  • Does map run the transform when you call it, or later?
    Later — only when the returned provider is queried (get/getOrNull/etc.), which is what keeps the chain lazy.

saying these in an interview costs you the question

  • Saying map executes the transform immediately when called.
  • Claiming you must call get() to use a derived value at config time.

context