Kotlin

자바에서 코틀린 컬렉션으로

주코식딩 2024. 2. 13. 10:47

자바의 컬렉션은 private final 로 선언하더라도 내부의 데이터를 변경할 수 있다.

이는 컬렉션을 매개변수로 넘길때 나타나는 문제인데, 일반적인 타입(String, Integer)와 다르게 컬렉션은 실제 값이 아닌 주소값을 매개변수로 넘긴다.

 

따라서, 함수내에서만 존재하는 지역변수로 컬렉션을 사용하였더라도 변경된 컬렉션은 영구적으로 적용된다.

 

옛날에는 가변 컬렉션이 더 큰 메리트가 있어서 이런식으로 개발되었지만 현재는 불변 컬렉션을 선호한다고 한다.

이런 추세에 맞춰서 Kotlin에서는 불변 컬렉션을 지원한다. 

Java : Collection -> List(가변)

Kotlin : Collection -> List(불변) -> MutableList(가변)

 

우선, 가변 컬렉션과 불변 컬렉션에는 어떤 장단점이 있을까?

서로의 장단점이 직접적인 연관이 있기에 한번에 설명하겠다.

 

가변, 불변 컬렉션의 관계

가변컬렉션은 직접적으로 변경이 가능하다.

직접적으로 변경이 불가능 하다면 불변 컬렉션을 가변 컬렉션으로 변환 한 뒤에 값을 추가, 수정, 삭제 한 뒤에 다시 불변 컬렉션으로 변환해주어야 한다.

 

언뜻 보기에는 굉장히 불필요한 작업을 하는 것 같지만 우리가 Setter를 사용하지 않는 이유를 생각하면 된다.

나는 Setter를 사용하지 않는 가장 큰 이유는 협업에 혼란을 초래하기 때문 이라고 생각한다.

컬렉션 또한 제공하는 입장에서 Read만 하기를 기대했지만 일단 수정이 가능하니 무슨일이 벌어질 지 모른다.

강타입언어인 자바에서는 private과 final을 통해서 이를 제한 할 수 있지만 컬렉션만은 불가능하다니 조금 아이러니 하다.

 

하지만 자바에서도 이 문제를 인식했는지 Collections.unmodifiableList(...) 라는 함수를 지원하기 시작했다.

이 부분은 Optional과 비슷하게 느껴지지 않는가?

Optional<T>T?가 다른것 처럼 Collections.unmodifiableList(...)와 MutableList도 다르다.

 

 

Kotlin의 불변 컬렉션 단점

하지만 코틀린의 불변 컬렉션은 사실 완벽한 불변 컬렉션은 아니다.

이는 MutableList가 List의 하위타입이기에 생기는 문제점인데 코틀린에서도 가변이 아닌 객체의 공식 용어를 불변이라 표현하지 않고 읽기 전용이라고 표현한다고 한다.

 

예제를 통해 이해해 보자.

 

class ListTest(
    val list: List<String> = listOf()
)


fun test() {
    val muList: MutableList<String> = mutableListOf("a", "b", "c")

    val listTest = ListTest(list = muList)

    println(listTest.list)  // [a, b, c]

    muList.add("d")

    println(listTest.list)  // [a, b, c, d]
}

 

ListTest 클래스의 list변수는 불변 컬렉션으로 선언했지만 값이 변경되었다.

 

이 부분을 해결하기 위한 방법은 간단하다.

ListTest 클래스 내부에서 muList의 참조를 끊어버리면 된다.

 

class ListTest(
    list: List<String> = listOf()
) {
    val list: List<String> = list.toList()  // 불변 리스트로 복사
}


fun test() {
    val muList: MutableList<String> = mutableListOf("a", "b", "c")

    val listTest = ListTest(list = muList)

    println(listTest.list)  // [a, b, c]

    muList.add("d")

    println(listTest.list)  // [a, b, c]
}

 

ListTest 클래스의 생성자에서 제공받은 muList를 그대로 사용하는 것이 아닌 deepCopy를 진행해서 불변 컬렉션으로 변환한 작업이다.

물론 이렇게 된다면 메모리를 2배 사용한다는 단점이 있다.

 

 

조금 더 근본적인 방식으로 이 문제를 해결 할 수는 없었을까?

가변, 불변 객체 중 이 문제를 가장 이상적으로 해결한 케이스는 String와 StringBuilder라고 생각한다.

StringBuilder는 String의 하위 타입이 아니다.(대입이 불가능하다.)

 

StringBuilder를 String으로 변환하기 위해서는 반드시 .toString() 함수를 호출해야한다.

 

하지만 코틀린과 자바의 100% 호환성을 위해서 인지는 잘 모르겠지만 위의 문제점은 반드시 파악하고 있는 것이 좋다고 본다.

'Kotlin' 카테고리의 다른 글

영역 함수(scope functions)  (0) 2024.02.29
수신 객체 지정 람다  (0) 2024.02.29
코틀린의 정적 타입  (0) 2024.02.19
확장함수를 사용해 직관적으로 변경하기  (0) 2024.02.07
T?에 대해  (0) 2024.02.07