skip to content

When would you use tasks.matching { } to select tasks, and what are its trade-offs compared to withType()?

level: middleimportance: should knowfreq 40%

answer

  1. matching = predicate filter, live
  2. use for name/group, not type
  3. withType narrows first, then matching
  4. less lazy than withType
  5. selects existing, never creates

basics

~20 s

tasks.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 lines
kotlin
tasks.withType<Test>()
    .matching { it.name.contains("integration") }
    .configureEach {
        useJUnitPlatform()
        maxHeapSize = "2g"
    }

go deeper

for a junior

Know matching{} selects tasks by a predicate; basic awareness is enough.

for a middle

Explain when to use matching vs withType and chain them with configureEach.

for a senior

Reason about the avoidance cost of predicate evaluation and structure selectors type-first.

for a principal

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.

context