아이템 01. 가변성을 제한하라

아이템 01. 가변성을 제한하라

아이템 01. 가변성을 제한하라. #

val #

val 은 읽기 전용 프로퍼티지만, immutable 을 의미하는 것은 아니다. setter 를 제공하지 않을 뿐이다. (혼동하지 말 것)

immutable 이 필요하다면 final 키워드를 사용한다.


컬렉션 다운캐스팅 #

컬렉션 다운캐스팅은 계약을 위반하고, 추상화를 무시하는 행위다.

아래는 컬렉션 다운캐스팅의 간단한 예시이다.

val list = listOf(1,2,3)

if (list is MutableList) {
    list.add(4)
}

JVM에서 listOf 는 자바의 List 인터페이스를 구현한 Array.ArrayList 객체를 리턴한다.

자바의 List 인터페이스는 add, set 과 같은 메서드를 제공하니까 코틀린에서 MutableList 로 변경될 수 있다.

하지만 Arrays.ArrayList 이런 연산을 구현하고 있지 않아 오류가 발생한다.

즉, 위 코드의 문제는 플랫폼이나 내부 구현 버전(예를 들어, java/kotlin 언어의 버전 업그레이드)마다 결과가 달라질 수 있다.

" ‘시스템 해킹’을 시도해서 다운캐스팅을 할 때 문제가 됩니다. 실제로 코틀린 프로젝트를 진행할 때, 이는 허용해서는 안 되는 큰 문제입니다. “


immutable 객체로부터 mutable 객체가 필요한 경우 #

immutable 객체에 write 작업이 필요한 경우,

  1. 복제(copy)를 통해서 새로운 mutable 객체를 만들어 반환,사용한다.
  2. 새로운 객체를 만들어 반환,사용한다.

다른 종류의 변경 가능 지점 #

변경 가능한 컬렉션을 사용할 때 다음과 같은 조합으로 사용할 수 있다.

  1. val + mutable 컬렉션
  2. var + immutable 컬렉션
  3. var + mutable 컬렉션
  • 사용/비교하지 않는다.

(다른 종류의 변경 가능 지점) 예시 1. val + mutable 컬렉션 #

val list = mutableListOf<Int>()

list.add(1) 
println(list)           // [1]

list += 2               // list.plusAssign(2)로 변경된다.
println(list)           // [1, 2]

(다른 종류의 변경 가능 지점) 예시 2. var + immutable 컬렉션 #

var list = listOf<Int>()

list = list + 1
println(list)           // [1]

list += 2               // list.plus(2)로 변경된다.
println(list)           // [1, 2]
기준특징
val + mutable 컬렉션변경 지점/코드가 컬렉션 구현 내부에 있다.

멀티스레드 처리가 이루어질 경우, 내부적으로 적절한 동기화가 되어 있는지 확실하게 알 수 없으므로 위험하다.
var + immutable 컬렉션변경 지점/코드가 우리 코드에 있다.

멀티스레드 처리 안정성이 더 좋다고 할 수 있다. (즉, 우리가 직접 제어하기 편하다.)

예시 1: (우리가 관리하는 코드라면) 값이 변경될 때 Delegates.observable를 사용해서 log를 추가하거나 이벤트를 발행하거나 등의 추가적인 행위를 쉽게 덧붙일 수 있다.

예시 2. setter 를 private 으로 만들 수 있다.

“물론 잘못 만들면 일부 요소가 손실될 수 있다” 고 설명한다.

” mutable 컬렉션에도 observable 하게 만들 수 있지만 추가적인 구현이 필요하다. 따라서 mutable 프로퍼티에 immutable 컬렉션을 사용하는 것이 더 쉽다. “

(Q) 내부적으로 구현된 컬렉션이 어떤 타입인지 정확히 알고 있다면 구현된 컬렉션을 사용하는 것이 더 좋지 않을까?

사용자가 개발한 코드에 오류가 발생하기 더 쉽지 않을까?


변경 가능 지점 노출하지 말기 #

(한 줄 요약) mutable 객체를 외부에 노출하는 것은 굉장히 위험하다.

mutable 객체를 외부에 노출하고 싶다면 아래와 같이 처리할 수 있다.

(1) 방어적 복제(copy)
(2) immutable 반환


요약 #

  • val, final 혼동하지 말 것
  • 컬렉션 다운캐스팅 하지말 것
  • 쓰기가 필요한 컬렉션을 프로퍼티로 가질 때 var + immutable 컬렉션 조합으로 사용하자.



최근에 관련해서 고민한 부분이 있었는데 도움이 됐다.