skip to content

How do you obtain Property and Provider instances in a plugin, and why can't you instantiate them directly?

level: middleimportance: should knowfreq 40%

answer

  1. Provider/Property are interfaces, no new
  2. project.objects.property(Type::class)
  3. @Inject ObjectFactory in managed types
  4. project.provider { } for read-only
  5. abstract managed properties = Gradle instantiates

basics

~10 s

They're interfaces with no public constructor. You create a Property via ObjectFactory (project.objects.property(Type::class)) and create read-only Providers via project.provider { ... } or by transforming an existing Provider.

solid answer

~40 s

`Provider<T>` and `Property<T>` are **interfaces**, so you never `new` them — Gradle owns their implementations. For a writable `Property`, ask the `ObjectFactory`, available as `project.objects`: `objects.property(String::class)`. ObjectFactory is also injectable into task and extension constructors via `@Inject`, which is the preferred way inside managed types so Gradle wires instances for you. For a read-only `Provider` computed from a closure, use `project.provider { ... }`; for one derived from an existing provider, use transformations that return a new `Provider`. Creating through the factory (rather than a constructor) lets Gradle attach the laziness machinery, presence tracking, and configuration-cache support that a hand-rolled object couldn't provide. In a custom task or extension you typically declare these as abstract `@get:Input`/`@Internal` properties and let Gradle's managed-property mechanism instantiate them automatically.

code

kotlin · 5 lines
kotlin
abstract class StampExtension @Inject constructor(objects: ObjectFactory) {
    val version: Property<String> = objects.property(String::class)
    // or, fully managed:
    // abstract val version: Property<String>
}

go deeper

for a junior

Know that you create a Property via project.objects.property(Type::class), not a constructor.

for a middle

Distinguish ObjectFactory for writable Property vs project.provider for read-only Provider; mention @Inject.

for a senior

Recommend abstract managed properties as the idiomatic style and explain why Gradle owns the implementations (laziness + config cache).

for a principal

Standardize managed-property authoring conventions across the team's plugins to keep types config-cache compatible and consistent.

## Why no constructor `Property<T>` and `Provider<T>` are Gradle **interfaces**. Their concrete implementations carry internal state for lazy evaluation, presence, conventions, and configuration-cache serialization. Gradle deliberately hides those classes so it can evolve them and guarantee the laziness contract — hence you always go through a factory. ## ObjectFactory — the source of Properties `ObjectFactory` is a Gradle service exposed as `project.objects`. Relevant methods: - `property(Class<T>)` — a `Property<T>`. - `listProperty/mapProperty/setProperty` — collection variants (separate leaf). - `fileProperty()`, `directoryProperty()` — file-typed variants (separate leaf). ```kotlin val objects = project.objects val name: Property<String> = objects.property(String::class) val count: Property<Int> = objects.property(Int::class) ``` ## Injecting ObjectFactory Inside a custom task or extension, prefer constructor injection so the object is managed: ```kotlin abstract class GreetingExtension @Inject constructor(objects: ObjectFactory) { val message: Property<String> = objects.property(String::class) } ``` ## Read-only Providers When you just need a lazily-computed read-only value: - `project.provider { expensiveCompute() }` — wraps a closure. - Transforming an existing provider returns a new `Provider` (still read-only). ## Managed (abstract) properties — the modern style Instead of creating instances yourself, declare abstract getters and let Gradle implement them: ```kotlin abstract class MyExtension { abstract val message: Property<String> // Gradle provides the instance } ``` Gradle's managed-type mechanism instantiates the backing `Property` automatically when it creates the extension/task. This is the cleanest approach and avoids touching ObjectFactory at all. ## Summary Writable `Property` -> `objects.property(...)` or an abstract managed getter. Read-only `Provider` -> `project.provider { }` or a transformation. Never a constructor — Gradle owns the implementations.

  • What's the advantage of declaring an abstract Property instead of creating one via ObjectFactory?
    Gradle's managed-type mechanism instantiates the backing property for you, so there's less boilerplate, no @Inject of ObjectFactory, and it's the idiomatic style for tasks and extensions.
  • How do you make a read-only Provider from a computation?
    Use project.provider { ... } to wrap a closure that returns the value lazily, or derive it from an existing provider via a transformation — both return a Provider you cannot set().

saying these in an interview costs you the question

  • Trying to call a Property/Provider constructor.
  • Storing the resolved value in a plain field instead of using ObjectFactory.
  • Confusing project.provider { } (read-only) with objects.property (writable).

context