backend/Kotlin

[Kotlin] 가변 컬렉션과 읽기 전용 컬렉션 구분하기

버리야 2022. 8. 2. 20:00
반응형

이펙티브 코틀린 책을 읽고 간단히 정리해본 것입니다.

아이템1. 가변성을 제한하라. - 가변 컬렉션과 읽기 전용 컬렉션 구분하기 중에서 




읽기 전용  컬렉션
Iterable, Collection, Set, List 인터페이스 

읽고 쓸수 있는 컬렉션
MutableIterable, MutableCollection, MutableSet, MutableList 인터페이스
- 읽기전용 인터페이스를 상속 받아, 변경을 위한 메서드만 추가.

Iterable 과 MutableIterable 인터페이스

package kotlin.collections

import kotlin.internal.PlatformDependent

/**
 * Classes that inherit from this interface can be represented as a sequence of elements that can
 * be iterated over.
 * @param T the type of element being iterated over. The iterator is covariant in its element type.
 */
public interface Iterable<out T> {
    /**
     * Returns an iterator over the elements of this object.
     */
    public operator fun iterator(): Iterator<T>
}

/**
 * Classes that inherit from this interface can be represented as a sequence of elements that can
 * be iterated over and that supports removing elements during iteration.
 * @param T the type of element being iterated over. The mutable iterator is invariant in its element type.
 */
public interface MutableIterable<out T> : Iterable<T> {
    /**
     * Returns an iterator over the elements of this sequence that supports removing elements during iteration.
     */
    override fun iterator(): MutableIterator<T>
}



코틀린에서 컬렉션을 진짜로 불변(immutable)하게 만들지 않고 읽기 전용으로 설계한 것은 더 많은 자유를 얻을 수 있다.
코틀린은 내부적으로 immutable 하지 않은 컬렉션을 외부적으로 Immutable하게 보이게 만들어서 얻어지는 안정성이 있지만, 개발자가 '시스템 해킹'을 시도해서 다운캐스팅을 할때가 문제가 된다.
리스트를 읽기 전용으로 리턴하면 이를 읽기전용으로만 사용해야 한다. 

컬렉션 다운캐스팅은 계약을 위반하고 추상화를 무시하는 행위. 코드가 안전하지 않고, 예측하지 못한 결과를 초래한다.

fun main() {
    val list = listOf(1, 2, 3)

    // 이렇게 하면 안된다.
    if (list is MutableList) {
        list.add(4)
    }
}



읽기 전용 컬렉션을 mutable로 변경해야 한다면, 

복제(copy)를 통해서 새로운 mutable 컬렉션을 만드는 list.toMutableList를 활용해야 한다.

fun main() {
    val list = listOf(1, 2, 3)

    val mutableList = list.toMutableList()
    mutableList.add(4)
}



User라는 immutable 객체가 있고, 성(surname)을 변경해야 한다면, withSurname과 같은 메서드를 제공해서, 자신을 수정한 새로운 객체를 만들어 낼 수 있게 해야한다.


fun main() {
    var user = User("Maja","Markewic2")
    user = user.withSurname("Moskata")
    print(user) // User(name=Maja,surname=Moskata)
}

class User(
    val name: String,
    val surname: String
) {
    fun withSurname(surname: String) = User(name, surname)
}



반응형