xref: /aosp_15_r20/external/kotlinpoet/docs/properties.md (revision 3c321d951dd070fb96f8ba59e952ffc3131379a0)
1Properties
2==========
3
4Like parameters, properties can be created either with builders or by using convenient helper
5methods:
6
7```kotlin
8val android = PropertySpec.builder("android", String::class)
9  .addModifiers(KModifier.PRIVATE)
10  .build()
11
12val helloWorld = TypeSpec.classBuilder("HelloWorld")
13  .addProperty(android)
14  .addProperty("robot", String::class, KModifier.PRIVATE)
15  .build()
16```
17
18Which generates:
19
20```kotlin
21class HelloWorld {
22  private val android: String
23
24  private val robot: String
25}
26```
27
28The extended `Builder` form is necessary when a field has KDoc, annotations, or a field
29initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code
30blocks above:
31
32```kotlin
33val android = PropertySpec.builder("android", String::class)
34  .addModifiers(KModifier.PRIVATE)
35  .initializer("%S + %L", "Oreo v.", 8.1)
36  .build()
37```
38
39Which generates:
40
41```kotlin
42private val android: String = "Oreo v." + 8.1
43```
44
45By default `PropertySpec.Builder` produces `val` properties. Use `mutable()` if you need a
46`var`:
47
48```kotlin
49val android = PropertySpec.builder("android", String::class)
50  .mutable()
51  .addModifiers(KModifier.PRIVATE)
52  .initializer("%S + %L", "Oreo v.", 8.1)
53  .build()
54```
55
56## Inline properties
57
58The way KotlinPoet models inline properties deserves special mention. The following snippet of code:
59
60```kotlin
61val android = PropertySpec.builder("android", String::class)
62  .mutable()
63  .addModifiers(KModifier.INLINE)
64  .build()
65```
66
67will produce an error:
68
69```
70java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on
71properties. You should mark either the getter, the setter, or both inline.
72```
73
74Indeed, a property marked with `inline` should have at least one accessor which will be inlined by
75the compiler. Let's add a getter to this property:
76
77```kotlin
78val android = PropertySpec.builder("android", String::class)
79  .mutable()
80  .getter(
81    FunSpec.getterBuilder()
82      .addModifiers(KModifier.INLINE)
83      .addStatement("return %S", "foo")
84      .build()
85  )
86  .build()
87```
88
89The result is the following:
90
91```kotlin
92var android: kotlin.String
93  inline get() = "foo"
94```
95
96Now, what if we wanted to add a non-inline setter to the property above? We can do so without
97modifying any of the code we wrote previously:
98
99```kotlin
100val android = PropertySpec.builder("android", String::class)
101  .mutable()
102  .getter(
103    FunSpec.getterBuilder()
104      .addModifiers(KModifier.INLINE)
105      .addStatement("return %S", "foo")
106      .build()
107  )
108  .setter(
109    FunSpec.setterBuilder()
110      .addParameter("value", String::class)
111      .build()
112  )
113  .build()
114```
115
116We get the expected result:
117
118```kotlin
119var android: kotlin.String
120  inline get() = "foo"
121  set(`value`) {
122  }
123```
124
125Finally, if we go back and add `KModifier.INLINE` to the setter, KotlinPoet can wrap it nicely and
126produce the following result:
127
128```kotlin
129inline var android: kotlin.String
130  get() = "foo"
131  set(`value`) {
132  }
133```
134
135Removing the modifier from either the getter or the setter will unwrap the expression back.
136
137If, on the other hand, KotlinPoet had allowed marking a property `inline` directly, the programmer
138would have had to manually add/remove the modifier whenever the state of the accessors changes in
139order to get correct and compilable output. We're solving this problem by making accessors the
140source of truth for the `inline` modifier.
141
142 [formatter]: https://developer.android.com/reference/java/util/Formatter.html
143