코틀린은 어떻게 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
....생략 ....
}