How do you combine two providers into one in Gradle while keeping the result lazy, and what happens if one input is absent?
answer
- zip(other) { a, b -> ... }
- lazy combine of two providers
- either absent => result absent
- combiner gets non-null pair
- carries both dependencies
basics
~10 sUse 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 linesval 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 lazilygo deeper
Know zip exists to combine two providers lazily.
Explain the combiner signature, that both must be present, and that dependencies are preserved.
Choose between zip, flatMap, and ListProperty aggregation, and avoid eager get()-based combining.
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.