Why should you prefer tasks.named<Test>("test") { } over tasks.getByName("test") { } when configuring an existing task in the Kotlin DSL?
answer
- named = lazy lookup
- getByName = eager lookup
- typed receiver via named<Test>
- deferred configuration action
- realized only when needed
basics
~10 snamed() configures the task lazily via a TaskProvider, so it's only realized if needed. getByName() realizes the task immediately, defeating configuration avoidance.
solid answer
~40 s`tasks.named<Test>("test") { ... }` returns a `TaskProvider<Test>` and registers a *deferred* configuration action: the block runs only when the `test` task is actually realized. `tasks.getByName("test") { ... }` looks the task up *eagerly*, forcing it to be created and its configuration to run right now, even on unrelated invocations. The typed `named<Test>` overload also gives you a `Test`-typed receiver inside the block, so you get type-safe access to properties like `maxParallelForks` and `useJUnitPlatform()` without casting. The rule mirrors register-vs-create: `named` is the configuration-avoidance way to touch an existing task; `getByName` is the eager escape hatch you avoid unless a plugin forces it.
code
kotlin · 8 lines// Lazy + type-safe receiver
tasks.named<Test>("test") {
useJUnitPlatform()
maxParallelForks = Runtime.getRuntime().availableProcessors()
}
// Eager — avoid
tasks.getByName<Test>("test").maxParallelForks = 4go deeper
Recall that named is the lazy way to configure an existing task and is preferred.
Explain deferred configuration actions, the typed receiver, and what triggers realization.
Discuss keeping whole dependency chains lazy via providers and auditing scripts for stray getByName/getByType.
Drive a lint/convention enforcing lazy lookups across all build logic and convention plugins; quantify configuration-time savings.
## Configuring tasks others registered Plugins register many tasks for you — `test`, `jar`, `compileKotlin`, `assemble`. When you need to tweak one, you must *look it up*. The Kotlin DSL offers two families: ### Lazy (preferred) - `tasks.named("test")` → `TaskProvider<Task>` - `tasks.named<Test>("test")` → `TaskProvider<Test>` (typed) - `tasks.named<Test>("test") { ... }` → registers a deferred configuration action and returns the provider The configuration block is stored and only executed when the task is *realized* (scheduled to run or forced via `.get()`). On a `help` invocation, the `test` task is never realized, so your tweak costs nothing. ### Eager (avoid) - `tasks.getByName("test")` → realizes and returns the `Task` now - `tasks.getByName<Test>("test")` → same, typed - `tasks.getByName("test") { ... }` → realizes now and configures now ## Type safety The typed overload `named<Test>("test")` not only stays lazy but supplies a correctly typed receiver. Without it you'd get a `Task` and have to cast: ```kotlin // typed + lazy — best tasks.named<Test>("test") { useJUnitPlatform() maxParallelForks = 4 } ``` ## Realization rules A `TaskProvider` is realized when: - the task is on the execution graph (requested or a dependency of a requested task), - you call `.get()` on the provider, - a type-safe accessor like `tasks.test` is referenced (these are eager today), - an eager API such as `getByName`, `withType<T> { }` (non-configureEach), or `tasks.all { }` runs. ## Wiring without realizing You can use the returned provider to express dependencies or input wiring without realizing the task: ```kotlin val testTask = tasks.named<Test>("test") tasks.named("check") { dependsOn(testTask) } ``` This keeps the whole chain lazy until the build actually needs `check`.
- Does named() execute its configuration block at the point you call it?No. It records a deferred configuration action that runs only when the task is realized. The call itself returns a provider immediately.
- What advantage does the typed named<Test>("test") overload give beyond laziness?A Test-typed receiver inside the block, so properties like maxParallelForks and useJUnitPlatform() are available without an explicit cast.
- When would getByName still be justified?When a legacy plugin's API hands you no provider, or you genuinely need the realized instance synchronously — rare in modern builds.
saying these in an interview costs you the question
- Saying named() and getByName() behave identically.
- Believing named()'s block runs immediately when you call named().