skip to content

How do you combine two providers into one in Gradle while keeping the result lazy, and what happens if one input is absent?

level: middleimportance: should knowfreq 42%

answer

  1. zip(other) { a, b -> ... }
  2. lazy combine of two providers
  3. either absent => result absent
  4. combiner gets non-null pair
  5. carries both dependencies

basics

~10 s

Use providerA.zip(providerB) { a, b -> ... }. It builds a new lazy provider combining both values. If either input is absent, the combiner is skipped and the result is absent.

solid answer

~40 s

`Provider<A>.zip(other: Provider<B>, combiner: (A, B) -> R): Provider<R>` combines two providers into a derived `Provider<R>`. Like `map`/`flatMap`, the combiner runs lazily — only when the result is queried — and both source providers are resolved at that moment. **Absence short-circuits:** if *either* input provider has no value, the combiner is not invoked and the result is absent, so the combiner always receives two non-null arguments. The zipped provider also carries the task dependencies of *both* inputs, which makes it ideal for joining, say, a version string and a build directory into a final artifact path. For more than two inputs you nest zips or, more readably, combine a `ListProperty`. Because zip stays lazy and dependency-aware, it's the correct tool instead of `pA.get() + pB.get()` at configuration time.

code

kotlin · 4 lines
kotlin
val version = objects.property(String::class.java)
val suffix  = objects.property(String::class.java).convention("SNAPSHOT")
val fullVersion: Provider<String> = version.zip(suffix) { v, s -> "$v-$s" }
// fullVersion is absent until version has a value; combiner then runs lazily

go deeper

for a junior

Know zip exists to combine two providers lazily.

for a middle

Explain the combiner signature, that both must be present, and that dependencies are preserved.

for a senior

Choose between zip, flatMap, and ListProperty aggregation, and avoid eager get()-based combining.

for a principal

Standardize derived-value composition across a plugin suite so consumers never hand-wire dependencies.

## The zip operator `zip` is the lazy-API way to fuse two independent providers: ``` Provider<A>.zip(right: Provider<B>, combiner: BiFunction<A, B, R>): Provider<R> ``` It returns a provider that, when queried, resolves both `A` and `B`, then applies the combiner. It behaves exactly like `map` with respect to laziness and dependency carrying, but with two upstreams. ## Absence semantics The combiner is invoked **only if both** inputs have a value. If either is absent, the zipped provider is absent and the combiner is skipped. This guarantees the combiner never sees a missing value and means a zip is, in effect, an AND over presence. ## Dependency carrying If `A` comes from `taskA`'s output and `B` from `taskB`'s output, the zipped provider depends on both `taskA` and `taskB`. Wiring it into a consumer task schedules both producers first — no manual `dependsOn`. ```kotlin val version = objects.property(String::class.java).convention("1.0.0") val buildDir = layout.buildDirectory val artifactPath: Provider<RegularFile> = version.zip(buildDir) { v, dir -> dir.file("app-$v.jar") }.flatMap { it } // when the combiner returns a value; use map form when it returns a RegularFile directly ``` In practice you'd write the combiner to return the value directly: ```kotlin val artifactPath: Provider<RegularFile> = version.zip(layout.buildDirectory) { v, dir -> dir.file("app-$v.jar") } ``` ## When zip vs flatMap vs ListProperty - Two independent inputs, combine into one value → `zip`. - Derive a value that is itself a provider from a single input → `flatMap`. - Many homogeneous inputs to aggregate → collect into a `ListProperty` and `map` over the list. Avoid `getOrElse`/`get` to read both sides eagerly — that defeats laziness and drops dependencies.

  • If only one of the two zipped providers is set, what is the zipped result?
    Absent. zip requires both inputs to be present; otherwise the combiner is skipped and the result has no value.
  • How would you combine three or more providers?
    Nest zip calls, or aggregate the values into a ListProperty/MapProperty and map over the collection for readability.
  • Does zip preserve task dependencies?
    Yes — the result carries the producer-task dependencies of both inputs, so wiring it into a consumer orders both producers first.

saying these in an interview costs you the question

  • Saying zip uses the present value when one input is absent.
  • Reading both providers with get() at configuration time to combine them.

context