면접 질문 목록으로 가기
면접 질문실전 질문꼬리 질문

val은 읽기 전용이지 불변이 아닌 이유

skydovesJaewoong Eum (skydoves)||7분 소요

val은 읽기 전용이지 불변이 아닌 이유

Kotlin에서 val 키워드는 불변성(immutability)을 보장한다고 흔히 오해받지만, 실제로는 초기화 이후 참조 재할당만 금지할 뿐입니다. 참조가 가리키는 객체 자체의 변경까지 막아 주지는 않습니다. 이 참조 불변성(reference immutability)과 객체 가변성(object mutability)의 차이는 Kotlin 타입 시스템을 정확히 이해하고, 동시성 코드나 상태에 민감한 코드에서 미묘한 버그를 예방하는 데 매우 중요합니다. 면접에서도 val이 "불변"이 아닌 "읽기 전용"임을 명확히 구분하여 설명할 수 있으면 기본기에 대한 깊은 이해를 보여줄 수 있으므로, 반드시 숙지해 두시길 권장합니다. 이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.

  • 읽기 전용 참조(read-only reference)와 불변 객체(immutable object)의 차이를 명확히 설명할 수 있습니다.
  • val로 선언한 가변 객체가 자체 API를 통해 어떻게 수정될 수 있는지 보여줄 수 있습니다.
  • 커스텀 getter가 val 프로퍼티의 반환값을 매번 다르게 만들 수 있는 원리를 설명할 수 있습니다.
  • Kotlin에서 진정한 불변성을 달성하기 위한 전략을 파악할 수 있습니다.

참조 불변성 vs 객체 가변성

val로 변수를 선언하면, Kotlin 컴파일러는 해당 참조의 재할당을 금지합니다. 참조 자체는 고정되지만, 참조가 가리키는 객체가 내부 상태를 변경하는 메서드를 노출하고 있다면 객체 내용은 얼마든지 바뀔 수 있습니다.

val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)  // 허용: 리스트 내용이 변경됨
// mutableList = mutableListOf(5, 6)  // 컴파일 오류: 참조 재할당 불가

mutableList 참조는 항상 동일한 MutableList 인스턴스를 가리킵니다. 하지만 해당 인스턴스에 add()를 호출하면 리스트 내용이 변경됩니다. 즉, val 키워드는 객체 내부에서 무슨 일이 벌어지는지에 대해서는 어떤 제약도 두지 않습니다.

이 동작은 Java의 final과 본질적으로 동일합니다. final 변수는 재할당할 수 없지만, 참조하는 객체는 공개 API를 통해 여전히 수정할 수 있습니다. 면접에서 Java와의 유사성을 함께 언급하면 더 넓은 시야를 가진 답변이 됩니다.

동시성 코드에서는 이 구분이 더욱 중요해집니다. val 참조는 참조 자체가 변경되지 않으므로 스레드 간 공유가 안전합니다. 하지만 참조가 가리키는 객체가 가변이고, 여러 스레드가 동시에 객체를 수정한다면 val로 선언했더라도 경쟁 조건(race condition)이 발생할 수 있습니다.

val sharedMap = mutableMapOf("key" to "value")
// Thread 1: sharedMap["key"] = "updated"
// Thread 2: sharedMap["key"] = "conflict"
// val 선언에도 불구하고 경쟁 조건 발생 가능

불변 맵이나 ConcurrentHashMap과 같은 스레드 안전 구현체를 사용하면 이 문제를 해결할 수 있지만, val 키워드만으로는 객체의 동시 수정에 대한 보호를 전혀 제공하지 못합니다. 따라서 동시성 환경에서는 val 선언 여부와 별개로, 객체 자체의 스레드 안전성을 반드시 확보해야 합니다.

커스텀 Getter와 계산 프로퍼티(Computed Properties)

이 면접 질문은 구독자 전용입니다

Dove Letter를 구독하시면 안드로이드, 코틀린 개발 관련 독점 면접 질문의 전체 내용을 볼 수 있습니다.

구독하기
면접 질문 목록으로 가기