Kotlin Backing Field
in Programming Language on Kotlin
코틀린에서 상수 선언하는법
코틀린에서 상수를 선언하려고 할 때 자바를 쓰던 사람들이 가장 많이 하는 실수는 아래와 같다.
class Test {
const val TEST_CONST = "TEST"
}
자바를 써오던 사람들은 의아해한다.
엥 이게 왜 상수 선언이 안될까? 라고.
동시에 인텔리제이에서는 다음과 같은 에러를 표시한다.
직역하면 Const val은 오직 오브젝트 최상단에만 허락되어진다.
즉,
const val TEST_CONST = "TEST"
class Test {
}
코틀린에선 상수를 표현할 때 위와 같이 표현되어야한다.
왜일까?
우선 Kotlin에서 const 키워드는 코틀린 클래스에서 프로퍼티로 인식하지 않는다.
프로퍼티는 기본적으로 getter, setter가 존재하는데 const의 경우 불변값이므로 setter가 존재할 수 없다.
그럼 getter는 존재할 수 있지않느냐! 라고 반박할 수 있다.
여기서 오늘의 주제인 Backing Field가 등장한다.
Kotlin Backing Field
Backing Field란 무엇일까? 직역하자면 백업 필드정도 되겠다.
우선 다음과 같은 예시를 보자.
class Test {
val str = "Hello"
get() = str + "World"
}
위 예시에서 우리는 일반적으로 메인 코드에서 다음과 같이 호출했을 때 Hello World가 출력되는 것을 기대할 수 있다.
fun main() {
val test = Test()
println(test.str)
}
그러나 실제 출력 결과는 에러를 발생시키다가 Stack Overflow
를 발생시키며 펑!하고 프로그램이 터진다.
자 그럼 문제가 무엇이길래 터지는 걸까?
get()의 경우 str을 호출했을 때 호출되는 코틀린의 프로퍼티 getter 메소드이다. (setter도 같은 방법으로 선언 가능)
그렇기 때문에 test.str
은 사실 test.str.get()
이 생략되었다고 볼 수 있다.
일반적으로 val은 참조 불변의 성질을 가진 데이터 타입이기 때문에 상수와 쉽게 혼동하게 된다.
그러나 val은 참조 불변의 성질을 갖고 있지만 값 자체가 바뀌지 않는 것을 의미하지는 않는다.
그 반례가 바로 위와 같이 getter()에 문자열을 더하는 경우이다.
그렇기 때문에 val를 상수로 보기 보다는 읽기 전용 데이터 타입이라고 보는게 맞겠다.
어쨌든, 다시 처음 문제로 돌아와서 test.str
이 get() 메소드를 호출하기 때문에, getter 내부에서 str을 호출하는 것 또한 재귀적으로 자기 자신의 get() 메소드를 호출하게 된다.
그렇게 무한히 함수를 호출하다 스택이 펑 터지게 되는 것이다.
이 때 사용할 수 있는 것이 바로 Backing Field이다.
getter 또는 setter에서 자기 자신을 호출하는 경우
field라는 특별한 이름의 필드를 사용할 수 있다.
class Test {
val test = "Hello"
get() = field + "World"
}
위 메소드는 HelloWorld
를 출력하게 될 것이다.
이것이 BackingField이다.
처음으로 돌아와서
class Test {
const val TEST_CONST = "TEST"
}
우리는 처음 왜 클래스 안에서는 상수 선언이 안될까에 대해서 의문을 가졌었다.
그 이유는 간단하다, val 프로퍼티는 const 상수 키워드를 붙일 경우 그 프로퍼티가 불변하다는 것을 보장해줄 수 없다.
왜냐하면 위와 같이 getter에 BackingField를 사용하면 얼마든지 반환 값을 바꿀 수 있기 때문이다.
그렇기 때문에 상수라는 본연의 의미를 벗어나게 되고 이를 인지한 코틀린에서 const는 아예 클래스 내부의 프로퍼티가 아닌 최상위 전역 변수로 만들라는 에러를 내보내는 것이다.
마치며
val == 상수 == 참조 불변
그동안 코틀린에서 val의 의미를 위와 같이 인지하고 있었는데 단단히 잘못알고 있었다는 사실을 알게 되었다.
왜 const를 클래스 내부에서 사용할 수 없는지, 왜 전역으로 빼야하는지, Backing Field가 뭔지를 이해하고 나니 내가 얼마나 어리석은 착각을 하였는지 알 수 있게 되었다.
이 모든 과정을 설명해주시느라 바쁜 시간을 내주신 bernard에게 진심으로 감사드립니다.