When would you use tasks.matching { } to select tasks, and what are its trade-offs compared to withType()?
answer
- matching = predicate filter, live
- use for name/group, not type
- withType narrows first, then matching
- less lazy than withType
- selects existing, never creates
basics
~20 stasks.matching { } returns a live collection of tasks satisfying an arbitrary predicate — e.g. by name prefix. Use it when a type filter isn't enough. The trade-off: its predicate is less configuration-avoidance-friendly than withType's pure type filter.
solid answer
~40 s`tasks.matching { it.name.startsWith("publish") }` returns a **live, filtered `TaskCollection`** of every task for which the predicate is true, now or in the future. You typically chain `.configureEach { }` to configure them lazily. Use `matching` when selection depends on something other than type — name patterns, a property value, a group. The catch: the predicate runs against each task, so `matching` is inherently less lazy than `withType<T>()`, which filters purely on type metadata without inspecting task state. As a rule, prefer `withType` when a type filter suffices and reach for `matching` only when you need predicate-based selection. Combining them is common: `tasks.withType<Test>().matching { it.name.contains("integration") }.configureEach { ... }` narrows by type first (cheap), then by predicate.
code
kotlin · 6 linestasks.withType<Test>()
.matching { it.name.contains("integration") }
.configureEach {
useJUnitPlatform()
maxHeapSize = "2g"
}go deeper
Know matching{} selects tasks by a predicate; basic awareness is enough.
Explain when to use matching vs withType and chain them with configureEach.
Reason about the avoidance cost of predicate evaluation and structure selectors type-first.
Establish conventions discouraging broad matching predicates that hurt configuration time across many projects.
## matching: predicate-based selection `tasks.matching { spec }` returns a **live `TaskCollection`** containing exactly the tasks for which the closure/predicate returns `true`. Like `withType`, the collection is live — tasks added later that satisfy the predicate join it automatically. You almost always chain a lazy configuration: ```kotlin tasks.matching { it.name.startsWith("generate") }.configureEach { group = "codegen" } ``` Use it when the selection criterion is **not** a type: a name prefix/suffix, membership in a task group, or any computed condition. ## Trade-off vs withType `withType<T>()` filters by **type metadata**, which Gradle can evaluate without realizing or deeply inspecting tasks — it is the most avoidance-friendly filter. `matching { }` must evaluate your predicate against tasks, which can force more work and is therefore considered less lazy. Best practice: filter by type first with `withType`, then refine with `matching` only for the residual predicate: ```kotlin tasks.withType<Test>() .matching { it.name.contains("integration") } .configureEach { useJUnitPlatform() } ``` Here `withType<Test>()` cheaply narrows to test tasks, and `matching` only inspects that smaller set. ## matching is not register `matching` *selects existing* tasks; it never creates them. If no task currently matches, the configuration simply applies to nothing now and to future matches as they appear. ## Summary - `matching { predicate }` → live collection by arbitrary condition. - Chain `.configureEach { }` for lazy configuration. - Less avoidance-friendly than `withType`; prefer `withType` for type filters and narrow with `matching` when needed.
- Is the collection returned by matching {} live or a snapshot?Live — tasks added later that satisfy the predicate are automatically included.
- Why chain withType before matching?withType filters cheaply by type metadata, shrinking the set the predicate must evaluate, which is more configuration-avoidance-friendly.
saying these in an interview costs you the question
- Claiming matching {} creates tasks (it only selects existing ones).
- Asserting matching is just as lazy as withType for all cases.