@@ -36,10 +36,11 @@ class C {
36
36
<!-- - TOC -->
37
37
38
38
* [ Use Cases] ( #use-cases )
39
- * [ Expose read-only subtype ] ( #expose-read-only-subtype )
39
+ * [ Expose read-only supertype ] ( #expose-read-only-supertype )
40
40
* [ Decouple storage type from external representation] ( #decouple-storage-type-from-external-representation )
41
41
* [ Expose read-only view] ( #expose-read-only-view )
42
42
* [ Access field from outside of getter and setter] ( #access-field-from-outside-of-getter-and-setter )
43
+ * [ Retrieve property delegate reference] ( #retrieve-property-delegate-reference )
43
44
* [ Design] ( #design )
44
45
* [ Explicit Backing Fields] ( #explicit-backing-fields )
45
46
* [ Restrictions] ( #restrictions )
@@ -58,16 +59,17 @@ class C {
58
59
59
60
## Use Cases
60
61
61
- This proposal caters to a variety of use-cases that are currently met via a backing property pattern.
62
+ This proposal caters to a variety of use-cases that are currently met via a
63
+ [ backing property pattern] ( https://kotlinlang.org/docs/properties.html#backing-properties ) .
62
64
63
- ### Expose read-only subtype
65
+ ### Expose read-only supertype
64
66
65
67
We often do not want our data structures to be modified from outside. It is customary in Kotlin to have
66
68
a read-only (e.g. ` List ` ) and a mutable (e.g. ` MutableList ` ) interface to the same data structure.
67
69
68
70
``` kotlin
69
71
internal val _items = mutableListOf<Item >()
70
- val item : List <Item > by _items
72
+ val item: List <Item > by _items
71
73
```
72
74
73
75
And the new syntax allows us to write:
@@ -77,6 +79,9 @@ val items: List<Item>
77
79
internal field = mutableListOf ()
78
80
```
79
81
82
+ > While the number of lines in this code does not change, the related properties become better grouped together and
83
+ repetition of the property name multiple times is avoided.
84
+
80
85
This use-case is also widely applicable to architecture of reactive applications:
81
86
82
87
* Android ` LiveData ` has a ` MutableLiveData ` counterpart.
@@ -145,27 +150,83 @@ val state: StateFlow<State>
145
150
field = MutableStateFlow (State .INITIAL )
146
151
```
147
152
148
- For this use-case, the TBD syntax of [ Direct Backing Field Access] ( #direct-backing-field-access ) will need to be added.
153
+ > Note, that ` field.asStateFlow() ` allocates a wrapper read-only view object and it is called here every time this
154
+ property is accessed, allocating a new instance. For some applications this is an acceptable and even preferable
155
+ behavior. For others, it is crucial to create this view object once and cache it in a separate field. We don't plan
156
+ to address the later use-cases &mdash ; that's where the backing-field pattern will have to continue to be used.
157
+
158
+ For this use-case to become actually usable, the TBD syntax of [ Direct Backing Field Access] ( #direct-backing-field-access )
159
+ will need to be added. That's because such applications need to modify the mutable data structure that is stored in the field,
160
+ but the rules of [ Smart Type Narrowing] ( #smart-type-narrowing ) will not make it directly available.
149
161
150
162
### Access field from outside of getter and setter
151
163
152
164
Kotlin allows property field to be accessed from the property's getter and setter using a ` field ` variable.
153
- This proposal is designed with an idea to provide an explicit syntax to access a property's field from anywhere
154
- inside the corresponding class when the field is declared explicitly:
155
165
156
166
``` kotlin
157
167
class Component {
158
168
var status: Status
159
- field // explicit field declaration
160
169
set(value) {
161
170
field = value
162
171
notifyStatusChanged()
163
172
}
164
173
}
165
174
```
166
175
167
- This way, all the code inside the class can change the field of the property directly, without invoking the setter.
168
- However, the actual access syntax of such [ Direct Backing Field Access] ( #direct-backing-field-access ) is TBD.
176
+ This proposal is designed with an idea to provide an explicit syntax to access a property's field from anywhere
177
+ inside the corresponding class as if the backing field was declared with ` private ` access. In this particular example,
178
+ any code inside the class ` Component ` will be able to change the field of the ` status ` property directly, without invoking the setter.
179
+ However, the actual access syntax of such [ Direct Backing Field Access] ( #direct-backing-field-access ) is TBD.
180
+
181
+ ### Retrieve property delegate reference
182
+
183
+ Delegated properties in Kotlin have a backing field that stores a reference to the delegate object and
184
+ automatically generate getter and setter implementations that forward accesses to the delegate.
185
+ The following Kotlin code:
186
+
187
+ ``` kotlin
188
+ class Data {
189
+ val expensive: T by lazy { /* ...*/ }
190
+ }
191
+ ```
192
+
193
+ is basically compiled into the following code:
194
+
195
+ ``` kotlin
196
+ class Data {
197
+ private val expensive$delegate: Lazy <T > = SynchronizedLazyImpl <T > { /* ...*/ }
198
+ val expensive: T
199
+ get() = expensive$delegate.value
200
+ }
201
+ ```
202
+
203
+ As you can see, ` expensive$delegate ` here is effectively a backing for the property with the delegate.
204
+ The are many use-cases where the direct access to the delegate is need. For example, a ` Lazy ` type
205
+ has [ ` isInitialized ` ] ( https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-lazy/is-initialized.html ) function.
206
+ The existing syntax for its access is cumbersome and not type-safe:
207
+
208
+ ``` kotlin
209
+ fun isExpensiveInitialized () =
210
+ (this ::value.getDelegate() as Lazy <T >).isInitialized()
211
+ ```
212
+
213
+ In practice, it means that
214
+ [ developers have to resort to an backing field pattern] ( https://stackoverflow.com/questions/42522739/kotlin-check-if-lazy-val-has-been-initialised )
215
+ to make it look nice:
216
+
217
+ ``` kotlin
218
+ class Data {
219
+ private val expensiveDelegate: Lazy <T > = SynchronizedLazyImpl <T > { /* ...*/ }
220
+ val expensive: T
221
+ get() = expensiveDelegate.value
222
+ fun isExpensiveInitialized () =
223
+ expensiveDelegate.isInitialized()
224
+ }
225
+ ```
226
+
227
+ It is expected that TBD [ Direct Backing Field Access] ( #direct-backing-field-access ) syntax is going to
228
+ provide a direct and simpler way to access the delegate of a delegated property without having to
229
+ resort to the backing field pattern.
169
230
170
231
## Design
171
232
@@ -329,8 +390,8 @@ Attempt to add support for the above syntax led to multiple redundant complicati
329
390
330
391
### Direct Backing Field Access
331
392
332
- We plan to add support for a syntax to explicitly access the property's backing field when the field was
333
- explicitly declared and is accessible via some TBD syntax.
393
+ We plan to add support for a syntax to explicitly access the property's backing field as well as for property
394
+ delegate via some TBD syntax.
334
395
335
396
### Protected Fields
336
397
0 commit comments