Java-Bom / ReadingRecord

📚 책 읽고 정리하기 📚
https://javabom.tistory.com/category/Reading%20Record
472 stars 27 forks source link

[아이템 16] backing field #254

Closed chldbtjd2272 closed 2 years ago

chldbtjd2272 commented 2 years ago

질문 : 프로퍼티의 setter에서 backing field로 값을 변경하지 않으면 재귀호출로 실패하는데 자세한 내용 설명 추가해주세요

jyami-kim commented 2 years ago

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를 호출하는 것과 같다.

image

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

image

따라서 getter, setter 모두 본인의 필드를 참조하는 경우에는 StackOverflowException 을 발생시키게 된다. 친절하게도 intellij 에서는 recursive call 이라고 안내를 해주고 있다.

추가로 kotlin을 kotlinc를 사용하여 생성된 바이트코드를 보면 어떤 경우에 backing field가 생성되는지를 볼 수 있다. 즉 backing 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;

  // 함수들의 어셈블러 코드