프로그래밍중급 안드로이드 개발자

코틀린에서 컬렉션과 객체의 불변성(immutability)은 어떻게 구현되며, 실천은 자바와 어떻게 다릅니까?

Hintsage AI 어시스턴트로 면접 통과

답변

문제의 역사:

자바에서 컬렉션의 불변성은 Collections.unmodifiableList()와 immutable 클래스를 수동으로 설계하는 방법(final 필드, 세터 없음)을 통해 달성됩니다. 안드로이드 및 서버 개발에서 이는 비용이 많이 들고 불편해졌으며, 스레드 안전성과 데이터의 예측 가능성이 손실되었습니다.

문제:

자바 컬렉션은 기본적으로 안전하지 않습니다. 객체의 참조를 항상 얻고 수정할 수 있습니다. 이는 특히 멀티스레드 환경에서 불변성을 제어하는 것을 복잡하게 하여, 잡기 힘든 버그를 초래합니다.

해결책:

코틀린은 처음부터 read-only와 mutable 컬렉션의 분리를 고려하여 설계되었으며, truly immutable 객체 생성을 위해 val, data class 및 mutable 접두사가 없는 List/Set/Map을 쉽게 사용할 수 있도록 하였습니다. 이 위에 copy() 메소드를 통한 복사 활용이 추가되었습니다.

코드 예:

val nums: List<Int> = listOf(1, 2, 3) // 불변 리스트 // nums.add(4) // 컴파일 오류 val user = User(name = "Alex", age = 30) val updated = user.copy(age = 31) // 불변 map 예 val state: Map<String, Int> = mapOf("a" to 1, "b" to 2)

주요 특징:

  • read-only 컬렉션을 수정하려고 시도하면 컴파일 오류가 발생합니다.
  • 코틀린의 val은 참조의 불변성을 보장하지만, 객체의 불변성을 보장하지는 않습니다.
  • truly immutable을 위해서는 read-only 인터페이스만 사용하고 내부 상태의 변경을 금지해야 합니다.

함정 질문들.

val이 중첩 객체의 절대 불변성을 보장합니까?

아니요! val은 변수의 다른 객체를 가리킬 수 없다고 보장할 뿐, 객체의 필드는 변경할 수 있습니다(내부 속성이 var인 경우).

read-only 컬렉션에서 mutable 객체를 얻을 수 있습니까?

네 — 컬렉션이 mutable 객체를 포함하고 있다면, read-only 인터페이스의 컬렉션에도 불구하고 상태를 변경할 수 있습니다.

data class User(var name: String) val users: List<User> = listOf(User("Vasya")) users[0].name = "Petya" // 허용됨

List<Int>를 as?를 통해 MutableList<Int>로 변환하면 어떻게 됩니까?

컴파일되지만, 원래 타입이 read-only 인터페이스였다면 실행 중에 ClassCastException이 발생합니다.

일반적인 오류 및 안티 패턴

  • val을 객체의 절대 불변성 보장으로 오해하기
  • "immutable" 컬렉션 내의 mutable 객체에 대한 참조 조작
  • read-only 컬렉션을 mutable로 형변환하기

실제 예시

부정적인 사례

안드로이드 애플리케이션이 state를 val List<User>에 저장하여 불변이라고 생각하는 경우, 한 개발자가 user.name을 수정합니다. 다른 프래그먼트에서 업데이트된 리스트가 예상치 못한 일관성 없는 데이터를 표시하게 되며, 이는 릴리스에서 버그로 나타납니다.

장점:

  • 컬렉션에 대한 빠른 작업

단점:

  • 변이 제어 상실
  • 숨겨진 변경 가능성

긍정적인 사례

오직 val 필드만을 사용한 immutable data class를 사용하고, 내부 객체에 대한 접근을 필터링하며, copy()를 통해 상태를 업데이트합니다.

장점:

  • 높은 예측 가능성과 스레드 안전성
  • 예상치 못한 사이드 효과 없음

단점:

  • 때때로 더 많은 할당과 복사가 필요함
  • 모든 서드파티 라이브러리가 truly immutable 객체를 제공하지는 않음