Skip to content

Commit 31f6f8e

Browse files
committed
Fixes various issues after review
* Fix subtype -> supertype. * Clarify the advantages of the new syntax of the backing property pattern. * Clarify repeated allocations in Expose read-only view use-case. * Fix Access field from outside of getter and setter use-cases, don't require explicit field declaration. * Retrieve property delegate reference use-case added.
1 parent 35ca780 commit 31f6f8e

File tree

1 file changed

+73
-12
lines changed

1 file changed

+73
-12
lines changed

proposals/explicit-backing-fields.md

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ class C {
3636
<!--- TOC -->
3737

3838
* [Use Cases](#use-cases)
39-
* [Expose read-only subtype](#expose-read-only-subtype)
39+
* [Expose read-only supertype](#expose-read-only-supertype)
4040
* [Decouple storage type from external representation](#decouple-storage-type-from-external-representation)
4141
* [Expose read-only view](#expose-read-only-view)
4242
* [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)
4344
* [Design](#design)
4445
* [Explicit Backing Fields](#explicit-backing-fields)
4546
* [Restrictions](#restrictions)
@@ -58,16 +59,17 @@ class C {
5859

5960
## Use Cases
6061

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).
6264

63-
### Expose read-only subtype
65+
### Expose read-only supertype
6466

6567
We often do not want our data structures to be modified from outside. It is customary in Kotlin to have
6668
a read-only (e.g. `List`) and a mutable (e.g. `MutableList`) interface to the same data structure.
6769

6870
```kotlin
6971
internal val _items = mutableListOf<Item>()
70-
val item : List<Item> by _items
72+
val item: List<Item> by _items
7173
```
7274

7375
And the new syntax allows us to write:
@@ -77,6 +79,9 @@ val items: List<Item>
7779
internal field = mutableListOf()
7880
```
7981

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+
8085
This use-case is also widely applicable to architecture of reactive applications:
8186

8287
* Android `LiveData` has a `MutableLiveData` counterpart.
@@ -145,27 +150,83 @@ val state: StateFlow<State>
145150
field = MutableStateFlow(State.INITIAL)
146151
```
147152

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.
149161

150162
### Access field from outside of getter and setter
151163

152164
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:
155165

156166
```kotlin
157167
class Component {
158168
var status: Status
159-
field // explicit field declaration
160169
set(value) {
161170
field = value
162171
notifyStatusChanged()
163172
}
164173
}
165174
```
166175

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.
169230

170231
## Design
171232

@@ -329,8 +390,8 @@ Attempt to add support for the above syntax led to multiple redundant complicati
329390

330391
### Direct Backing Field Access
331392
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.
334395

335396
### Protected Fields
336397

0 commit comments

Comments
 (0)