aboutsummaryrefslogtreecommitdiff
path: root/docs/properties.md
blob: cb878ab2c0d97a68df08c0446ff711cddf2a0bba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
Properties
==========

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

```kotlin
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:

```kotlin
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()`][formatter]-like syntax as the code
blocks above:

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

Which generates:

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

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

```kotlin
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:

```kotlin
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:

```kotlin
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:

```kotlin
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:

```kotlin
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:

```kotlin
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:

```kotlin
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.

 [formatter]: https://developer.android.com/reference/java/util/Formatter.html