상태(state)
var을 사용하거나 mutable 객체를 사용하면 값이 변동될 여지가 생기게 됩니다. 이를 상태(state)라고 합니다.
상태를 가지게 되는 경우, 이제는 객체의 사용 방법뿐만이 아니라 객체의 상태에 대한 이력(history)에 의존하게 됩니다.
그렇기에 코틀린에서는 주로 3가지 방법을 사용해서 가변성을 제한합니다.
- val (읽기 전용 프로퍼티)
- 가변 컬렉션과 읽기 전용 컬렉션 구분하기
- 데이터 클래스의 copy
val
val을 사용해 읽기 전용 프로퍼티를 만들 수 있습니다. 다만 이러한 val의 값이 항상 변하지 않는다는 말은 아닙니다.
val이 mutable 하거나 getter를 정의해서 다른 변경 가능한 프로퍼티를 참조한다면, val 값도 변할 수 있습니다. 아래의 예시에서 person은 변할 수 있습니다.
val name: String = "준형"
var age: Int = 27
val person
get() = name to age
val의 값은 변경될 수 있지만, 프로퍼티 참조 자체(메모리 주소나 객체를 가리키는 포인터)를 변경할 수는 없으므로 동기화 문제를 줄일 수 있습니다.
가변 컬렉션과 읽기 전용 컬렉션 구분하기
코틀린의 컬렉션은 변경할 수 있느냐에 따라 구분됩니다. 읽고 쓸 수 있는 컬렉션에는 `Mutable`이 붙습니다.
`Mutable`이 붙는 인터페이스들은 각각의 컬렉션을 확장해서 읽고 쓰기 위한 메서드를 추가한 것입니다.
`MutableList`를 예를 들어 보겠습니다.
public interface MutableList<E> : List<E>, MutableCollection<E>
`MutableList`는 List와 MutableCollection을 상속받습니다. 이를 통해 List의 읽기 전용 기능과 MutableCollection의 수정 가능 기능을 모두 포함하여, 동작하도록 확장합니다.
public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
override fun iterator(): MutableIterator<E>
public fun add(element: E): Boolean
public fun remove(element: E): Boolean
public fun addAll(elements: Collection<E>): Boolean
public fun removeAll(elements: Collection<E>): Boolean
public fun retainAll(elements: Collection<E>): Boolean
public fun clear(): Unit
}
그러니 Immutable 컬렉션이 Mutable 컬렉션이 되는 다운캐스팅(DownCasting)은 코틀린에서 절대로 일어나서는 안 되는 일입니다.
메서드에서 Immutable 컬렉션을 반환했다면, 이를 읽기 전용으로만 사용해야 합니다. 만약 Mutable 하게 변경해야 한다면, Copy를 통해 새로운 Mutable 컬렉션을 만들어야 합니다.
`toMutableList()`는 안전하게 새로운 Mutable 컬렉션을 반환합니다.
public fun <T> Collection<T>.toMutableList(): MutableList<T> {
return ArrayList(this)
}
변경 가능 지점 노출 금지
객체를 변경할 수 있는 메서드나 지점은 외부에 노출해서는 안됩니다. 외부에 데이터를 노출할 필요가 있을 경우에는 Immuable한 객체를 노출합니다.
private val _errorFlow = MutableSharedFlow<Throwable>()
val errorFlow: SharedFlow<Throwable> get() = _errorFlow
'스터디 > 이펙티브 코틀린' 카테고리의 다른 글
아이템 18 - "코딩 컨벤션을 지켜라" (0) | 2025.01.06 |
---|---|
아이템 10- "단위 테스트를 만들어라" (0) | 2024.12.23 |