Skip to content

Properties

Like parameters, properties can be created either with builders or by using convenient helper methods:

val android = PropertySpec.builder("android", String::class)
  .addModifiers(KModifier.PRIVATE)
  .build()

val helloWorld = TypeSpec.classBuilder("HelloWorld")
  .addProperty(android)
  .addProperty("robot", String::class, KModifier.PRIVATE)
  .build()

Which generates:

class HelloWorld {
  private val android: String

  private val robot: String
}

The extended Builder form is necessary when a field has KDoc, annotations, or a field initializer. Field initializers use the same String.format()-like syntax as the code blocks above:

val android = PropertySpec.builder("android", String::class)
  .addModifiers(KModifier.PRIVATE)
  .initializer("%S + %L", "Oreo v.", 8.1)
  .build()

Which generates:

private val android: String = "Oreo v." + 8.1

By default PropertySpec.Builder produces val properties. Use mutable() if you need a var:

val android = PropertySpec.builder("android", String::class)
  .mutable()
  .addModifiers(KModifier.PRIVATE)
  .initializer("%S + %L", "Oreo v.", 8.1)
  .build()

Inline properties

The way KotlinPoet models inline properties deserves special mention. The following snippet of code:

val android = PropertySpec.builder("android", String::class)
  .mutable()
  .addModifiers(KModifier.INLINE)
  .build()

will produce an error:

java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on
properties. You should mark either the getter, the setter, or both inline.

Indeed, a property marked with inline should have at least one accessor which will be inlined by the compiler. Let’s add a getter to this property:

val android = PropertySpec.builder("android", String::class)
  .mutable()
  .getter(
    FunSpec.getterBuilder()
      .addModifiers(KModifier.INLINE)
      .addStatement("return %S", "foo")
      .build()
  )
  .build()

The result is the following:

var android: kotlin.String
  inline get() = "foo"

Now, what if we wanted to add a non-inline setter to the property above? We can do so without modifying any of the code we wrote previously:

val android = PropertySpec.builder("android", String::class)
  .mutable()
  .getter(
    FunSpec.getterBuilder()
      .addModifiers(KModifier.INLINE)
      .addStatement("return %S", "foo")
      .build()
  )
  .setter(
    FunSpec.setterBuilder()
      .addParameter("value", String::class)
      .build()
  )
  .build()

We get the expected result:

var android: kotlin.String
  inline get() = "foo"
  set(`value`) {
  }

Finally, if we go back and add KModifier.INLINE to the setter, KotlinPoet can wrap it nicely and produce the following result:

inline var android: kotlin.String
  get() = "foo"
  set(`value`) {
  }

Removing the modifier from either the getter or the setter will unwrap the expression back.

If, on the other hand, KotlinPoet had allowed marking a property inline directly, the programmer would have had to manually add/remove the modifier whenever the state of the accessors changes in order to get correct and compilable output. We’re solving this problem by making accessors the source of truth for the inline modifier.