How do you select an existing task by name in a Gradle build script, and why is tasks.named() preferred over tasks.getByName()?
answer
- named() returns TaskProvider (lazy)
- getByName() realizes immediately
- typed named<JavaCompile> avoids cast
- deferred config runs only if task realized
- configuration-avoidance API
basics
~10 sUse tasks.named('compileJava') to get a lazy reference. It is preferred over getByName() because named() doesn't force the task to be configured immediately — configuration runs only if the task is actually needed.
solid answer
~40 s`tasks.named('compileJava')` returns a `TaskProvider`, a lazy handle to the task. Any configuration you pass — `tasks.named<JavaCompile>('compileJava') { options.encoding = "UTF-8" }` — is deferred and runs only if the task ends up in the execution graph. `tasks.getByName('compileJava')` instead returns the realized `Task` *immediately*, forcing it to be created and configured during the configuration phase even when it will never run. On a large multi-project build, eagerly realizing every task you touch inflates configuration time. `named()` is part of Gradle's configuration-avoidance API; the typed form `named<JavaCompile>(...)` also gives you compile-time access to the task's properties. Rule of thumb: reach for `named()`, never `getByName()`, in new code.
code
kotlin · 7 lines// Lazy — preferred
tasks.named<JavaCompile>("compileJava") {
options.encoding = "UTF-8"
}
// Eager — avoid: realizes & configures compileJava right now
tasks.getByName("compileJava")go deeper
Know that named() is the lazy way to get a task and is preferred over getByName(); be able to write a basic named() block.
Explain the configuration-phase realization difference and use the typed named<T>() form to avoid casts.
Articulate the configuration-avoidance rationale and how named() composes with register() and configureEach for a fast configuration phase.
Set conventions banning eager accessors in a large monorepo and explain measurable configuration-time impact across many subprojects.
## The problem: eager vs lazy task access A Gradle build has two main phases: **configuration** (every build script runs, the task graph is built) and **execution** (only the requested tasks plus their dependencies actually run). Historically the task container exposed *eager* accessors — `tasks.getByName('x')`, `tasks.create('x')`, `tasks['x']` — that **realize** a task: they create the task object and run all of its configuration code right then, during the configuration phase, regardless of whether the task will ever execute. In a build with hundreds of tasks across many subprojects, realizing tasks you merely *referenced* wastes time on every invocation, including `./gradlew help`. ## tasks.named() — the lazy locator `tasks.named('compileJava')` returns a **`TaskProvider<Task>`**, a lazy reference. The actual task is realized only if and when something needs it — typically because it (or a task depending on it) is part of the execution graph. Configuration supplied through the provider is also deferred: ```kotlin tasks.named<JavaCompile>("compileJava") { options.encoding = "UTF-8" // runs only if compileJava is realized } ``` The **typed** overload `named<JavaCompile>("compileJava")` (Kotlin) / `named("compileJava", JavaCompile)` (Groovy) returns a `TaskProvider<JavaCompile>`, so inside the configuration block you get statically-typed access to `JavaCompile`-specific properties without a cast. If the name doesn't exist, `named()` fails fast with an `UnknownTaskException`. ## getByName() — the eager locator `tasks.getByName('compileJava')` returns a realized `Task` *now*. Use it only when you genuinely need the concrete task object immediately and lazy access is impossible (rare). In modern builds it is a code smell. ## Why it matters Configuration avoidance keeps the configuration phase cheap. Combined with `register()` (lazy creation) and `withType().configureEach {}` (lazy bulk configuration), `named()` lets a build configure only the slice of the task graph that a given invocation actually touches. ## Summary - `named(name)` → lazy `TaskProvider`; deferred configuration. - `named<T>(name)` → typed provider, no cast needed. - `getByName(name)` → eager `Task`, realized immediately — avoid.
- What exception does named() throw if the task name doesn't exist?UnknownTaskException — it fails fast during configuration rather than returning null.
- Does named() guarantee the task is never configured?No — it defers configuration. If the task ends up in the execution graph (requested or a dependency), it is realized and the deferred config block runs.
saying these in an interview costs you the question
- Claiming named() and getByName() are interchangeable with no performance difference.
- Saying named() returns the Task object directly (it returns a TaskProvider).