티스토리 뷰
Kotlin을 개발하다 보면 listOf()와 emptyList()를 자주 사용하게 되는데, 두 함수의 내부가 궁금해졌다. 결론부터 적어보자면, 표현식이 다를 뿐 listOf()와 emptyList()는 Immutable List를 만들어내는 함수들이다. 아래는 이에 대해 알아본 내용이니 시간 있다면 한 번 보는 것도 나쁘지는 않다.
일단 emptyList와 listOf는 모두kotlin.collections 패키지 안에 포함되어있다. Kotlin에서 제공하는 Collection 지원들인데, 그렇다면 어떤 것이 다를까?
일단 emptyList의 코드는 아래와 같다.
/**
* Returns an empty read-only list. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.emptyReadOnlyList
*/
public fun <T> emptyList(): List<T> = EmptyList
함수 emptyList의 반환값은 EmptyList라는 Object임을 알 수 있다. 그렇다면 EmptyList 오브젝트는 어떤 것일까?
internal object EmptyList : List<Nothing>, Serializable, RandomAccess {
private const val serialVersionUID: Long = -7390468764508069838L
override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty()
override fun hashCode(): Int = 1
override fun toString(): String = "[]"
override val size: Int get() = 0
override fun isEmpty(): Boolean = true
override fun contains(element: Nothing): Boolean = false
override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()
override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")
override fun indexOf(element: Nothing): Int = -1
override fun lastIndexOf(element: Nothing): Int = -1
override fun iterator(): Iterator<Nothing> = EmptyIterator
override fun listIterator(): ListIterator<Nothing> = EmptyIterator
override fun listIterator(index: Int): ListIterator<Nothing> {
if (index != 0) throw IndexOutOfBoundsException("Index: $index")
return EmptyIterator
}
override fun subList(fromIndex: Int, toIndex: Int): List<Nothing> {
if (fromIndex == 0 && toIndex == 0) return this
throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex")
}
private fun readResolve(): Any = EmptyList
}
결국 내용은 "아무것도 없는 빈 List"와 동일하다. List 인터페이스를 구현하고 있으며, override된 함수들을 모두 보면 비어있는 리스트의 반환 값들을 정적으로 선언해 놓았다. 즉 "비어있는 Immutable List"라고 보면 된다.
그럼 다음으로 listOf()를 확인해보자.
/**
* Returns an immutable list containing only the specified object [element].
* The returned list is serializable.
* @sample samples.collections.Collections.Lists.singletonReadOnlyList
*/
public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)
/**
* Returns a new read-only list of given elements. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.readOnlyList
*/
public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
/**
* Returns an empty read-only list. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.emptyReadOnlyList
*/
@kotlin.internal.InlineOnly
public inline fun <T> listOf(): List<T> = emptyList()
세 가지의 listOf 프로토타입이 있는데, 앞의 두 가지는 전달받은 값을 포함하는 ImmutableList라고 볼 수 있다. 그리고 아래 코드를 보면 아무런 인자도 전달받지 않은 listOf() 함수가 있는데 이 값이 emptyList()를 반환하는 것을 알 수 있다.
좀 더 명확하게 빈 리스트를 보여주기 위해서는 emptyList()를 쓰는 것이 코드리뷰와 같은 가독성에 있어서는 괜찮은 컨벤션이 되지 않을까 싶다. listOf()인데 인자가 없는 경우에 대해서는 리뷰 단계에서 누락과 같은 요인을 확인해볼 수도 있을 것 같다.
@inlineOnly가 있는 것으로 봐서 결국 listOf()로 작성된 코드는 오버헤드를 줄이기 위해 emptyList()로 치환될 것을 알 수 있다. inline에 대해서도 더 적어볼까 하였는데 내용이 꽤나 많아서 다른 글로 알아보는 것이 좋을 것 같다. 아마도 다음 글이 되지 않을까 싶다.
오랜만에 쓰는 글인데, 앞으로는 꼭 생소하거나 특별해보이는 이슈가 아니더라도 소소하게 이렇게 글을 남겨볼까 한다. 예전에는 검색했을 때 많이 나오는 내용이면 잘 안 쓰게 되었는데 이제는 있는 내용이더라도 찾아본 내용에 대해서는 내 방식대로 생각 정리해보는 게 어떨까 싶다. 그런 의미로 보시는 분이 보기에 글의 정성(?)은 좀 부족할 수 있으나 글을 쓰는 시간 동안의 생각정리에 좀 더 주안점을 두고, 기록 자체에 비중을 둘까 한다.
'Kotlin' 카테고리의 다른 글
코루틴 안내서 (Coroutine guide) - Kotlin Coroutines 읽기 (#2) (0) | 2023.01.19 |
---|---|
Kotlin Coroutines 읽기 (#1) (0) | 2023.01.19 |