Sangil's blog

https://github.com/ChoiSangIl Admin

코틀린은 어떻게 Java에서 불변객체를 생성할 수 있게 만들었을까? DEV / PROGRAMING

2022-10-22 posted by sang12



“해당 그림으로는 이해가 안돼서 조금 더 뜯어 볼려고?한 이야기“

불변 리스트를 만드는 listOf 함수를 따라가 보자.

public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)

내부적으로 singletonList란 java collection 객체를 리턴 해주는 것을 볼 수 있다. singletonList 함수는 java Collections의 SingletonList<e> Class를 반환한다.
-java Collections.java → SingletonList class

 private static class SingletonList<E>
        extends AbstractList<E>
        implements RandomAccess, Serializable {

        @java.io.Serial
        private static final long serialVersionUID = 3093736618740652951L;

        @SuppressWarnings("serial") // Conditionally serializable
        private final E element;

        ... 생략 ....     
    }
        

여기서 SingletonList는 AbstracList<E>를 상속 받고 있는 것을 볼 수 있다. 어라? 그러면 add를 사용 할 수 있는거 아니야? 라는 의문이 들었다.

직접 사용해보자!

fun main(){
    val americano = Coffee("americano", 5000)
    val mix = Coffee("mix", 300)
    val espresso = Coffee("espresso", 4000)

    //public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)
    val list : List<Coffee> = java.util.Collections.singletonList(americano);
    //list.add() 사용 못함

    var list2 = java.util.Collections.singletonList(americano);
    //list2.add(mix)  //호출가능 하지만 오류가남 listOf는 넘겨주는 객체가 1개일 때는 singletonList를 사용하기 때문에 오류 발생 java singletonList = 인덱스가 1개일인 리스트
}

data class Coffee(
    val name : String,
    val price : Int
)

ArrayList를 사용하는 Immutable List를 선하고 자바 클래스 타입을 찍어보면java.util.Arrays$ArrayList을 사용하는 것을 알 수 있다. 하지만 add를 사용할 수 없다(왜일까? - 뒤에..).

그렇다면 직접 자바의 asList를 만들어서 사용해보자. 그럼 add의 호출은 가능하나 컴파일 시점에서 오류가 난다. 코틀린에서 immutable list에서 여러개의 인자로 리스트를 만들 때 코틀린에서 내부적으로 asList를 이용해 객체를 생성하기 때문에 그렇다. (java의 asList 특성)

fun main(){
    //여러개의 값을 받는 리스트 선언
    val list3 : List<Coffee> = listOf(americano, mix)
    println("${list3.javaClass} $list3") // 출력결과 : class java.util.Arrays$ArrayList [Coffee(name=americano, price=5000), Coffee(name=mix, price=300)] 내부적으로 java ArrayList를 사용하는 것을 알 수 있다.
    //list3.add(espresso) 역시 사용 못함

    val list4 = java.util.Arrays.asList(americano, mix)
    println("${list4.javaClass} $list4")
    //list4.add(espresso) 호출은 가능하지만 오류가남 java의 asList 특성
}

data class Coffee(
    val name : String,
    val price : Int
)

어떻게 코틀린에서는 내부적으로 Java Class를 사용하면서, add 함수를 사용 못하게 만들었을까?
- kotlin lostOf function

public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)
-> val list : List<Coffee> = java.util.Collections.singletonList(americano);

정답은 이 부분에 있는거 같다. 자바의 java.util.Collections.singletonList 객체를 코틀린 List<T> 타입의 형태로 반환 해준다

fun main(){
    //여러개의 값을 받는 리스트 선언
    val list3 : List<Coffee> = listOf(americano, mix)
    println("${list3.javaClass} $list3") // 출력결과 : class java.util.Arrays$ArrayList [Coffee(name=americano, price=5000), Coffee(name=mix, price=300)] 내부적으로 java ArrayList를 사용하는 것을 알 수 있다.
    //list3.add(espresso) 역시 사용 못함

    val list4 = java.util.Arrays.asList(americano, mix)
    println("${list4.javaClass} $list4")
    //list4.add(espresso) 호출은 가능하지만 오류가남 java의 asList 특성
}

data class Coffee(
    val name : String,
    val price : Int
)

어떻게 코틀린에서는 내부적으로 Java Class를 사용하면서, add 함수를 사용 못하게 만들었을까?
- kotlin lostOf function

public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)
-> val list : List<Coffee> = java.util.Collections.singletonList(americano);

정답은 이 부분에 있는거 같다. 자바의 java.util.Collections.singletonList 객체를 코틀린 List<T> 타입의 형태로 반환 해준다

어떻게 Java 클래스가 코틀린 타입으로 리턴 받을 수 있을까?
→ 코틀린도 결국 Java로 변환 되니 Java의 List와 호환되는 인터페이스로 변환이 되는걸까?
(코틀린에서 Java에 호환되는 인터페이스를 만들어서 사용해보자 요걸 해보면 좀 더 이해가 쉽지 않을까 싶다.. 다음에 해보자 )

처음에 해당 사항이 궁금해서 리서치 중, 코틀린의 List 인터페이스의 구현체가 없을까? 라는 생각으로 접근 했었습니다. 그러다가 찾은 js에서의 ArrayList 구현체.
https://github.com/JetBrains/kotlin/blob/f3385fbdcb8b9dc29661a8a4973c855cdcf73767/libraries/stdlib/js/src/kotlin/collections/ArrayList.kt#L15

kotlin의 List 인터페이스를 보면 add가 없는 것을 볼 수 있다.
결국 코틀린은 Java의 List를 사용하나 코틀린 인터페이스에서 이를 제공해주지 않아서 사용 할 수 없게 만들어 불변(immutable)을 지킨다고 볼 수 있다.

-Collections.kt

public interface List<out E> : Collection<E> {
    // Query Operations

    override val size: Int
    override fun isEmpty(): Boolean
    override fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>

    // Bulk Operations
    override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean

    // Positional Access Operations
    /**
     * Returns the element at the specified index in the list.
     */
    public operator fun get(index: Int): E

    ....생략 ....
}

1
2023-01-21 01:40:24

1

답글
1
2023-01-21 01:40:32

e

답글
1
2023-01-21 01:40:38

e

답글
1
2023-01-21 01:41:51

e

답글
REPLY