backingField와 recursive call | Backing Field and Recursive Call
Backing field
class User(name: String) {
var name: String = name
get() = name
set(value) {name = value}
}
위와 같은 클래스가 있다고 할 때, 코틀린에서 해당 property를 get 혹은 set 할 때 재귀호출이 일어나게 된다.
public fun main(){
println(User("mj").name)
User("mj").name = "jyami"
}
위와 같이 name 프로퍼티를 접근하는 것이 getter를 부르는 것과 같기 때문에
결국get() = this.get() 과 같이, getter를 부르면서 다시 getter를 호출하는 것과 같다.

마찬가지로 name 프로퍼티를 할당하는 것도 setter를 부르는 것과 같아서. set()=this.set("jyami") 다시 setter를 호출하게 된다.

따라서 getter, setter 모두 본인의 필드를 참조하는 경우에는 StackOverflowException 을 발생시키게 된다. 친절하게도 intellij 에서는 recursive call 이라고 안내를 해주고 있다.
추가로 kotlin을 kotlinc를 사용하여 생성된 바이트코드를 보면 어떤 경우에 backing field가 생성되는지를 볼 수 있다. 즉 backing field가 인스턴스 변수로 생성되는 경우는 아래와 같다.
- 하나이상의 기본 접근자를 사용하는 경우 (getter, setter)
- 커스텀하게 만든 접근자에서
field를 사용하는 경우.
data class HttpResponse(val body: String, var headers: Map<String, String>) {
val hasBody: Boolean
get() = body.isNotBlank()
var statusCode: Int = 100
set(value) {
if (value in 100..599) field = value
}
}
body, header는 기본 접근자를 이유로, statusCode는 커스텀 접근자를 이유로 생성되는데, hasBody는 그렇지 않다. (필드로 생성되지 않는다.)
javap -c -p com.kakao.talk.HttpResponse
Compiled from "BackingField.kt"
public final class com.jyami.HttpResponse {
private final java.lang.String body;
private java.util.Map<java.lang.String, java.lang.String> headers;
private int statusCode;
// 함수들의 어셈블러 코드
Backing field
class User(name: String) {
var name: String = name
get() = name
set(value) {name = value}
}
Given a class like the one above, in Kotlin, accessing the property via get or set will cause a recursive call.
public fun main(){
println(User("mj").name)
User("mj").name = "jyami"
}
Since accessing the name property is essentially the same as calling its getter,
it ends up being equivalent to get() = this.get() — calling the getter triggers the getter again.

Likewise, assigning to the name property is the same as calling its setter, so set()=this.set("jyami") ends up calling the setter again.

So if both the getter and setter reference their own field, it will throw a StackOverflowException. Thankfully, IntelliJ is kind enough to warn you with a "recursive call" message.
Additionally, if you look at the bytecode generated by kotlinc, you can see when a backing field is actually created. In other words, a backing field is generated as an instance variable in the following cases:
- When at least one default accessor is used (getter, setter)
- When a custom accessor uses the
fieldidentifier.
data class HttpResponse(val body: String, var headers: Map<String, String>) {
val hasBody: Boolean
get() = body.isNotBlank()
var statusCode: Int = 100
set(value) {
if (value in 100..599) field = value
}
}
body and headers get backing fields because they use default accessors, and statusCode gets one because of its custom accessor — but hasBody does not. (It is not generated as a field.)
javap -c -p com.kakao.talk.HttpResponse
Compiled from "BackingField.kt"
public final class com.jyami.HttpResponse {
private final java.lang.String body;
private java.util.Map<java.lang.String, java.lang.String> headers;
private int statusCode;
// Assembler code for functions
'Develop > Kotlin' 카테고리의 다른 글
| Kotlin Void vs Unit vs Nothing | Kotlin Void vs Unit vs Nothing (0) | 2022.05.02 |
|---|---|
| Ktor 찍먹하기 - 간단 HTTP API 작성 | Trying Out Ktor - Writing a Simple HTTP API (1) | 2021.12.11 |
댓글
Comments