skip to content

What are common pitfalls with -P values regarding empty/boolean values and reading absent properties?

level: middleimportance: nice to knowfreq 30%

answer

  1. project properties are always String
  2. -Pflag → empty string, not true
  3. hasProperty for presence flags
  4. property() throws, findProperty() null
  5. providers...getOrElse for defaults

basics

~10 s

-P values are always strings. -Pflag with no =value yields an empty string, not a boolean true. Reading an absent property with project.property() throws; findProperty() returns null.

solid answer

~40 s

Three traps. First, **everything is a string**: `-PmaxWorkers=4` gives the string "4", so compare or convert deliberately (`prop.toInt()`), never assume a number or boolean. Second, **bare `-Pflag` is not boolean true** — it sets the property to an empty string, so `if (project.hasProperty("flag"))` is truthy even though the value is ""; test presence with `hasProperty`/`findProperty != null`, not the value, when you mean a flag. Third, **absent-property access differs**: `project.property("x")` throws `MissingPropertyException` if `x` was never set, while `project.findProperty("x")` returns null and `providers.gradleProperty("x")` yields a Provider whose `.orNull` is null. The same string-typing and presence semantics apply whether the property came from `-P`, `ORG_GRADLE_PROJECT_`, or gradle.properties, since they all feed the one project-property table.

code

kotlin · 8 lines
kotlin
// presence flag (don't read the value):
val ci = project.hasProperty("ci")          // -Pci  -> true, value is ""

// typed value with default:
val workers = (findProperty("workers") as String?)?.toInt() ?: 2

// mandatory value, fail fast:
val version = project.property("version")    // throws if absent

go deeper

for a junior

Recall that -P values are strings and that property() throws while findProperty() returns null.

for a middle

Explain the bare-flag-empty-string trap and choose the right accessor for mandatory vs optional values.

for a senior

Standardize parsing/defaulting via providers.gradleProperty().getOrElse and presence-based flags, noting all channels share one string-typed table.

for a principal

Mandate consistent property-reading helpers across builds so typing and presence semantics are uniform and misconfiguration fails fast.

## All project properties are strings Whatever channel sets a project property — `-Px=4`, `ORG_GRADLE_PROJECT_x=4`, or `x=4` in gradle.properties — the value arrives as a **`String`**. There is no implicit numeric or boolean coercion: ```kotlin val workers = (findProperty("maxWorkers") as String?)?.toInt() ?: 2 val debug = findProperty("debug")?.toString().toBoolean() // "true" -> true ``` ## Bare -Pflag sets an empty string, not true Writing `-PenableX` with no `=value` registers `enableX` with the value `""` (empty string). Therefore: - `project.hasProperty("enableX")` → **true** (it exists) - the value is `""`, which is NOT the boolean `true` So a flag check must test **presence**, not the value: ```kotlin val enabled = project.hasProperty("enableX") // presence-based flag // NOT: findProperty("enableX") == "true" (it's "") ``` If you instead want a true/false value, pass it explicitly: `-PenableX=true` and parse with `.toBoolean()`. ## Absent-property access semantics | Accessor | If property is missing | |----------|------------------------| | `project.property("x")` | throws `MissingPropertyException` | | `project.findProperty("x")` | returns `null` | | `project.hasProperty("x")` | returns `false` | | `providers.gradleProperty("x")` | Provider; `.orNull` = null, `.get()` throws | Pick the accessor that matches your intent: `property()` when the value is mandatory (fail fast), `findProperty()`/`providers...orNull` when it's optional with a default. ## Providing defaults cleanly ```kotlin val env = providers.gradleProperty("env").getOrElse("local") ``` ## Why this matters These pitfalls cause silent misconfiguration: a numeric option treated as a string, a flag that's always "on" because its empty value is truthy in a presence check used as a value check, or a build that throws on a missing optional property. Knowing the string-only typing and the three absent-access behaviors lets you read injected properties safely regardless of which channel set them.

  • What value does -PenableCache (no =) give the property enableCache?
    An empty string "". The property exists (hasProperty is true) but its value is not the boolean true, so use a presence check for flags.
  • Difference between project.property("x") and findProperty("x") when x is absent?
    property("x") throws MissingPropertyException; findProperty("x") returns null. Use property() for mandatory inputs and findProperty for optional ones.

saying these in an interview costs you the question

  • Assuming -P numeric/boolean values are typed rather than strings.
  • Treating a bare -Pflag as boolean true.
  • Using project.property() for an optional property and getting a surprise exception.

context