skip to content

Explain the difference between extensions.create(name, type) and extensions.create(name, type, constructorArgs...). When would you pass constructor arguments?

level: middleimportance: should knowfreq 40%

answer

  1. create(name, type) vs create(name, type, args...)
  2. explicit args + injected services combined
  3. @Inject constructor mixes both
  4. pass args for real collaborators only
  5. newInstance(type, args) for nested

basics

~20 s

Both register a named extension that Gradle instantiates. The overload with extra args passes them to the extension's constructor (after any injected services). You use it when the extension needs values not available via injection, like a reference to the owning project or a child container.

solid answer

~40 s

`extensions.create("name", MyExtension::class.java)` asks Gradle to instantiate `MyExtension` with **no explicit constructor arguments** — it still supplies injected services (e.g. an `@Inject ObjectFactory`). The overload `extensions.create("name", MyExtension::class.java, arg1, arg2)` passes `arg1, arg2` as **explicit constructor parameters**, which Gradle appends to (or interleaves with) the services it injects. You reach for the args overload when the extension genuinely needs collaborators it can't get by injection alone — for example handing it the `Project`, a parent extension, or a pre-built `NamedDomainObjectContainer` so it can expose nested DSL. Because Gradle still constructs the object, managed/abstract properties and `@Inject` services keep working alongside the explicit args. Prefer no-arg + injection where possible; only pass args for genuine dependencies, since fewer constructor params keep the extension instantiable and decoratable.

code

kotlin · 14 lines
kotlin
abstract class ReportExtension @Inject constructor(
    objects: ObjectFactory,        // injected
    val schemaVersion: String      // explicit
) {
    abstract val title: Property<String>
}

class ReportPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.extensions.create(
            "report", ReportExtension::class.java, "2024.1"
        )
    }
}

go deeper

for a junior

Know both overloads exist and that the second one passes values to the extension's constructor.

for a middle

Explain how explicit args combine with injected services and give a concrete reason to pass args (e.g. a config value or collaborator).

for a senior

Discuss trade-offs: injection vs explicit args for testability/coupling, and the analogous newInstance(type, args) for nested objects.

for a principal

Set conventions for plugin authors on minimizing explicit constructor surface to keep extensions stable, instantiable, and decoratable across versions.

## Two overloads The `ExtensionContainer.create` method has two relevant forms: ```kotlin // 1. No explicit args extensions.create("report", ReportExtension::class.java) // 2. Explicit constructor args extensions.create("report", ReportExtension::class.java, project, "v1") ``` In both cases Gradle — not you — instantiates the class via `ObjectFactory`, so decoration (managed properties) and `@Inject` service injection happen either way. ## How args combine with injection Gradle resolves the constructor by combining the **explicitly passed arguments** with the **services it can inject**. A common pattern is a constructor that takes both: ```kotlin abstract class ReportExtension @Inject constructor( private val objects: ObjectFactory, // injected by Gradle val version: String // passed explicitly via create(...) ) { abstract val title: Property<String> } ``` Here `objects` is injected; `version` comes from the `create(..., "v1")` call. Gradle matches the explicit args by type/position to the non-service parameters. ## When to pass args - The extension needs the **owning `Project`** (e.g., to create file collections relative to it). - It needs a **collaborator built by the plugin** — a shared service object, a parent extension, or a container the plugin set up. - It needs a **constant/config value** computed at apply time that isn't a Gradle service. ## When NOT to If the value can be obtained by injection (`ObjectFactory`, `ProjectLayout`, `ProviderFactory`, `Project`-scoped services), prefer injection — it keeps the constructor signature stable and decoupled. Overusing explicit args makes the extension harder to instantiate in tests and couples it to plugin internals. ## Nested objects For nested configurable objects created later, you don't use `create` args; you inject `ObjectFactory` and call `objects.newInstance(Nested::class.java, extraArg)` — the same arg-passing idea applies to `newInstance`. ## Gotcha If you pass an arg whose type collides with an injectable service, resolution can be ambiguous. Keep explicit args to plain values or clearly non-service types.

  • If a constructor has both an injected ObjectFactory and an explicit String, how does Gradle decide which is which?
    Gradle injects parameters whose types are known services (like ObjectFactory) and fills the remaining parameters positionally from the explicit args passed to create(...).
  • What's a downside of relying heavily on explicit constructor args?
    It couples the extension to plugin internals and makes it harder to instantiate independently (e.g., in unit tests via ObjectFactory.newInstance), versus depending only on injectable services.

saying these in an interview costs you the question

  • Claiming the args overload skips injection — injection still happens for service-typed parameters.
  • Saying you must pass ObjectFactory yourself as an explicit arg — it's injected automatically.

context