Skip to content

Commit db431bd

Browse files
authored
Add support for explicit backing fields (#2325)
* Add support for explicit backing fields * Update changelog
1 parent bf4a466 commit db431bd

4 files changed

Lines changed: 134 additions & 0 deletions

File tree

docs/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Change Log
33

44
## Unreleased
55

6+
* New: Support for explicit backing fields. (#2325)
67
* Fix: Keep the `//` prefix on wrapped file comment lines. (#1922)
78

89
## Version 2.3.0

kotlinpoet/api/kotlinpoet.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ public final class com/squareup/kotlinpoet/PropertySpec : com/squareup/kotlinpoe
750750
public static final fun builder (Ljava/lang/String;Lkotlin/reflect/KClass;[Lcom/squareup/kotlinpoet/KModifier;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
751751
public fun equals (Ljava/lang/Object;)Z
752752
public fun getAnnotations ()Ljava/util/List;
753+
public final fun getBackingFieldInitializer ()Lcom/squareup/kotlinpoet/CodeBlock;
754+
public final fun getBackingFieldType ()Lcom/squareup/kotlinpoet/TypeName;
753755
public fun getContextParameters ()Ljava/util/List;
754756
public fun getContextReceiverTypes ()Ljava/util/List;
755757
public final fun getDelegated ()Z
@@ -794,6 +796,10 @@ public final class com/squareup/kotlinpoet/PropertySpec$Builder : com/squareup/k
794796
public final fun addModifiers ([Lcom/squareup/kotlinpoet/KModifier;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
795797
public final fun addTypeVariable (Lcom/squareup/kotlinpoet/TypeVariableName;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
796798
public final fun addTypeVariables (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
799+
public final fun backingFieldInitializer (Lcom/squareup/kotlinpoet/CodeBlock;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
800+
public final fun backingFieldInitializer (Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
801+
public final fun backingFieldType (Lcom/squareup/kotlinpoet/TypeName;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
802+
public final fun backingFieldType (Lkotlin/reflect/KClass;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
797803
public final fun build ()Lcom/squareup/kotlinpoet/PropertySpec;
798804
public final fun delegate (Lcom/squareup/kotlinpoet/CodeBlock;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
799805
public final fun delegate (Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;

kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/PropertySpec.kt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ private constructor(
5252
public val getter: FunSpec? = builder.getter
5353
public val setter: FunSpec? = builder.setter
5454
public val receiverType: TypeName? = builder.receiverType
55+
public val backingFieldType: TypeName = builder.backingFieldType
56+
public val backingFieldInitializer: CodeBlock? = builder.backingFieldInitializer
5557

5658
init {
5759
require(
@@ -134,6 +136,17 @@ private constructor(
134136
setter.emit(codeWriter, null, implicitAccessorModifiers, false)
135137
codeWriter.emitCode("")
136138
}
139+
if (backingFieldType != type || backingFieldInitializer != null) {
140+
codeWriter.emitCode("")
141+
codeWriter.emit("field")
142+
if (backingFieldType != type) {
143+
codeWriter.emitCode(": %T", backingFieldType)
144+
}
145+
if (backingFieldInitializer != null) {
146+
codeWriter.emitCode(" = %L", backingFieldInitializer)
147+
}
148+
codeWriter.emitCode("")
149+
}
137150
}
138151

139152
internal fun fromPrimaryConstructorParameter(parameter: ParameterSpec): PropertySpec {
@@ -192,6 +205,8 @@ private constructor(
192205
internal var getter: FunSpec? = null
193206
internal var setter: FunSpec? = null
194207
internal var receiverType: TypeName? = null
208+
internal var backingFieldType: TypeName = type
209+
internal var backingFieldInitializer: CodeBlock? = null
195210

196211
public val modifiers: MutableList<KModifier> = mutableListOf()
197212
public val typeVariables: MutableList<TypeVariableName> = mutableListOf()
@@ -264,6 +279,47 @@ private constructor(
264279

265280
public fun receiver(receiverType: KClass<*>): Builder = receiver(receiverType.asTypeName())
266281

282+
/**
283+
* Specify the type of this property's backing field. If different from [type], an explicit
284+
* backing field will be emitted (see
285+
* [KEEP-0430](https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0430-explicit-backing-fields.md)):
286+
* ```kotlin
287+
* val city: LiveData<String> field: MutableLiveData<String>
288+
* ```
289+
*
290+
* Setting backing field type to the same value as [type] has no effect.
291+
*/
292+
public fun backingFieldType(type: TypeName): Builder = apply {
293+
this.backingFieldType = type
294+
}
295+
296+
/**
297+
* Specify the type of this property's backing field. If different from [type], an explicit
298+
* backing field will be emitted (see
299+
* [KEEP-0430](https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0430-explicit-backing-fields.md)):
300+
* ```kotlin
301+
* val city: LiveData<String> field: MutableLiveData<String>
302+
* ```
303+
*
304+
* Setting backing field type to the same value as [type] has no effect.
305+
*/
306+
public fun backingFieldType(type: KClass<*>): Builder = backingFieldType(type.asTypeName())
307+
308+
/**
309+
* Specify an explicit initializer for this property's backing field (see
310+
* [KEEP-0430](https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0430-explicit-backing-fields.md)).
311+
*/
312+
public fun backingFieldInitializer(codeBlock: CodeBlock): Builder = apply {
313+
this.backingFieldInitializer = codeBlock
314+
}
315+
316+
/**
317+
* Specify an explicit initializer for this property's backing field (see
318+
* [KEEP-0430](https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0430-explicit-backing-fields.md)).
319+
*/
320+
public fun backingFieldInitializer(format: String, vararg args: Any?): Builder =
321+
backingFieldInitializer(CodeBlock.of(format, *args))
322+
267323
// region Overrides for binary compatibility
268324
@Suppress("RedundantOverride")
269325
override fun addAnnotation(annotationSpec: AnnotationSpec): Builder =

kotlinpoet/src/jvmTest/kotlin/com/squareup/kotlinpoet/PropertySpecTest.kt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,4 +884,75 @@ class PropertySpecTest {
884884
.isInstanceOf<IllegalStateException>()
885885
.hasMessage("Using both context receivers and context parameters is not allowed")
886886
}
887+
888+
@Test
889+
fun explicitBackingFieldWithTypeOnly() {
890+
val propertySpec =
891+
PropertySpec.builder("city", ClassName(packageName = "", "LiveData").parameterizedBy(STRING))
892+
.mutable(false)
893+
.backingFieldType(ClassName(packageName = "", "MutableLiveData").parameterizedBy(STRING))
894+
.build()
895+
assertThat(propertySpec.toString())
896+
.isEqualTo(
897+
"""
898+
val city: LiveData<kotlin.String>
899+
field: MutableLiveData<kotlin.String>
900+
"""
901+
.trimIndent()
902+
)
903+
}
904+
905+
@Test
906+
fun explicitBackingFieldWithInitializerOnly() {
907+
val propertySpec =
908+
PropertySpec.builder("city", ClassName(packageName = "", "LiveData").parameterizedBy(STRING))
909+
.mutable(false)
910+
.backingFieldInitializer("%T()", ClassName(packageName = "", "MutableLiveData"))
911+
.build()
912+
assertThat(propertySpec.toString())
913+
.isEqualTo(
914+
"""
915+
val city: LiveData<kotlin.String>
916+
field = MutableLiveData()
917+
"""
918+
.trimIndent()
919+
)
920+
}
921+
922+
@Test
923+
fun explicitBackingFieldWithTypeAndInitializer() {
924+
val mutableLiveData = ClassName(packageName = "", "MutableLiveData")
925+
val propertySpec =
926+
PropertySpec.builder("city", ClassName(packageName = "", "LiveData").parameterizedBy(STRING))
927+
.mutable(false)
928+
.backingFieldType(mutableLiveData.parameterizedBy(STRING))
929+
.backingFieldInitializer("%T()", mutableLiveData)
930+
.build()
931+
assertThat(propertySpec.toString())
932+
.isEqualTo(
933+
"""
934+
val city: LiveData<kotlin.String>
935+
field: MutableLiveData<kotlin.String> = MutableLiveData()
936+
"""
937+
.trimIndent()
938+
)
939+
}
940+
941+
@Test
942+
fun explicitBackingFieldWithSameTypeAsProperty() {
943+
val liveDataOfString = ClassName(packageName = "", "LiveData").parameterizedBy(STRING)
944+
val propertySpec =
945+
PropertySpec.builder("city", liveDataOfString)
946+
.mutable(false)
947+
.backingFieldType(liveDataOfString)
948+
.build()
949+
assertThat(propertySpec.toString())
950+
.isEqualTo(
951+
"""
952+
val city: LiveData<kotlin.String>
953+
954+
"""
955+
.trimIndent()
956+
)
957+
}
887958
}

0 commit comments

Comments
 (0)