What is the Plugin<Project> interface in Gradle, and what does the apply method do?
answer
- single apply(Project) method
- register tasks / create extension / apply plugins
- called once at apply time
- binary form of build logic
- Gradle instantiates the class
basics
~10 sPlugin<Project> is the interface a Gradle plugin implements. Its single apply(project) method runs when the plugin is applied and is where you configure the project: register tasks, add extensions, or apply other plugins.
solid answer
~40 s`Plugin<T>` is Gradle's plugin interface; a project plugin implements `Plugin<Project>`. It has one method, `apply(Project project)`, called once when the plugin is applied to a project (via `plugins { id ... }` or `apply<MyPlugin>()`). Inside `apply` you mutate the project model: register tasks with `project.tasks.register(...)`, add an extension with `project.extensions.create(...)`, apply other plugins through `project.pluginManager.apply(...)`, and wire conventions. Gradle instantiates the plugin class (it needs a no-arg constructor, or constructor injection of services like `ObjectFactory`). The plugin is the *binary* form of build logic — compiled, reusable, and testable — as opposed to inline script code. Crucially, `apply` should set up lazy wiring, not eagerly run work.
code
kotlin · 8 linesclass GreetingPlugin : Plugin<Project> {
override fun apply(project: Project) {
val ext = project.extensions.create("greeting", GreetingExtension::class.java)
project.tasks.register("hello", HelloTask::class.java) { task ->
task.message.set(ext.message)
}
}
}go deeper
Know that Plugin<Project> has one apply(project) method and that's where you register tasks and extensions.
Explain instantiation, idempotent application, and binary-vs-script distinction; prefer lazy register.
Discuss constructor service injection, lazy wiring of extension Properties into task inputs, and avoiding eager work in apply.
Frame plugins as the org's reusable build-logic surface; standardize a plugin template (extension + lazy tasks + injected services) across teams.
## What a plugin is A Gradle plugin is a reusable, packaged unit of build logic. The contract is the generic interface `org.gradle.api.Plugin<T>`. For build logic targeting a project (the most common case) you implement `Plugin<Project>`. The type parameter `T` is the *target* the plugin configures — `Project` here, but `Settings` and `Gradle` are also valid targets for settings/init plugins. ```kotlin class GreetingPlugin : Plugin<Project> { override fun apply(project: Project) { // configure the project here } } ``` ## The single method: apply The interface has exactly one method: ``` void apply(T target) ``` Gradle calls `apply` **once**, at the moment the plugin is applied to the target. Application happens through the `plugins { }` block (`id "com.example.greeting"`), `apply(plugin = ...)`, or programmatically `project.pluginManager.apply(GreetingPlugin::class.java)`. Re-applying the same plugin type is idempotent — Gradle tracks applied plugins and won't run `apply` twice. ## What you do inside apply Three canonical jobs: 1. **Register tasks** — `project.tasks.register("hello", HelloTask::class.java)`. Use `register` (lazy) not `create` (eager). 2. **Add an extension** — `project.extensions.create("greeting", GreetingExtension::class.java)` to give users a configuration DSL block. 3. **Apply other plugins** — `project.pluginManager.apply("java")` so your plugin builds on top of existing capabilities. You typically also wire the extension's lazy `Property`/`Provider` values into the task inputs so user configuration flows to tasks without ordering problems. ## Instantiation Gradle constructs the plugin instance. A no-argument constructor works, or you can declare a constructor with `@Inject`-able services such as `ObjectFactory`, `ProviderFactory`, or `ProjectLayout`. Gradle supplies those. ## Binary vs script plugins This class form is a **binary plugin**: compiled Kotlin/Java/Groovy living in `buildSrc`, an included build, or a published artifact. It is type-safe, unit-testable (e.g. with ProjectBuilder), and reusable across projects — the preferred way to share non-trivial build logic.
- Why prefer tasks.register over tasks.create inside apply?register is lazy (configuration avoidance): the task is only configured/instantiated if it's actually needed for the build, keeping configuration time low. create eagerly realizes the task every build.
- Does apply run for every project automatically?No. apply runs only on the project(s) the plugin is applied to. Applying to the root project does not cascade to subprojects unless you explicitly iterate subprojects or each project applies it.
Think of apply() as a setup wizard that runs once: it installs the tasks, exposes the settings panel (extension), and pulls in dependencies (other plugins) — but it shouldn't actually do the heavy work itself; the tasks it registers do that later.
saying these in an interview costs you the question
- Saying apply() does the build work itself — it should register tasks, not execute them.
- Confusing Plugin<Project> with Plugin<Settings>; the target type matters.
- Using tasks.create eagerly when register is the modern lazy default.