코틀린 표준 라이브러리에 동일한 클래스가 2개?
728x90
반응형

⚠️ 경고 ⚠️

 

본 카테고리, Dev Memo는 필자가 깊게 다루기는 귀찮지만 궁금한 것들을 체험해보고 간단하게 기록을 남기는 공간입니다. 디버깅 노트, 써드파티 라이브러리 사용기, 버전 업데이트, 어이없는 실수, 오탈자 발견 등. 각종 시덥지 않은 내용이 들어 갈 수 있다는 점 참고바랍니다. (우헤헿)

 


 

문제 발견

Sequence 인터페이스를 학습하고 있었다. 공부하던 예시에서 구현한 라이브러리를 보려고 추적을 했는데 아래와 같이 2개의 파일을 발견했다. 아래 이미지에서 가장 왼쪽은 Sequence 인터페이스, 그리고 중간이 Sequence.kt, 오른쪽이 _Sequence.kt 파일이다. 왜 동일한 package에 언더바를 제외하면 동일한 이름의 파일이 2개나 있을까?

 

 

The Kotlin Standard Library

_Sequence.kt 파일의 주석에서 힌트를 얻을 수 있었다. 주석 내 Github 링크를 타고 들어가니 The Kotlin Standard Library에 대한 설명을 볼 수 있었다. 이 라이브러리를 패키지에서는 kotlin-stdlib라는 형태로 명명한다. 코틀린으로 개발할 때 필요한 함수와 자료구조를 가지고 있는 라이브러리로 Kotlin/JVM, Kotlin/JS용 라이브러리와 JDK 7 및 JDK 8용 라이브러리를 제공한다. 코틀린을 처음 학습할 때 기본적으로 세팅되어 있어야 하는 동시에 코틀린 기초 지식에 해당되는 개념이 포함되는 곳이다. 그리고 위 Sequence는 표준 라이브러리 중 공통화된 클래스를 모아놓은 kotlin-stdlib-common 라이브러리에서 확인할 수 있다. 이제 Github에서 The Kotlin Standard Library에 대한 설명(ReadMe.md)을 보자.

 

We use code generation to generate utility extension functions for some collection-like types like arrays, strings, Collection<T>, Sequence<T>, Map<K, V> etc.
These sources are placed into the generated folder and their names are prefixed with an underscore, for example, generated/_Collections.kt

code generation을 통해 배열, 문자열, Collection<T>, Sequence<T>, Map<K, V> 등과 같은 컬렉션을 위한 확장 함수을 생성한다.
이러한 소스 코드는 생성된 폴더에 배치되고 이름 앞에 밑줄(underscore)이 붙는다. (예: generated/_Collections.kt).

 

처음 의문을 가진 _Sequence.kt는 범용적인 확장 함수를 사용하기 위해 자동으로 생성되는 것이었다. 그런데 Sequence.kt에도 확장 함수가 있다. 그렇다면 이 두가지는 왜 분리되어 있을까?

 

 

Code Generation for Standard Library

위에서 언급한 Code Generation에서 힌트를 얻을 수 있다. 또한 _Sequence.kt 상단 주석에 아래와 같은 문구가 적혀있다. GenerateStandardLib는 kotlin-stdlib-gen에서 찾을 수 있다.

NOTE: THIS FILE IS AUTO-GENERATED by the GenerateStandardLib.kt
이 파일을 GenerateStandardLib.kt로 인해 자동 생성 되었다.

Code Generation for Standard Library에 대한 설명으로 아래와 같은 문장이 있다.

Some of the code in the standard library is created by code generation based on templates.
표준 라이브러리의 일부 코드는 템플릿 기반으로 code generation에 의해 생성된다.

kotlin-stdlib-gen의 하위에 여러개의 template가 존재하고 이를 통해 공통적인 코드를 자동으로 생성해 주는 것이다. 예를 들어 Sequence 템플릿을 통해 _Sequence.kt에 asIterable()와 asSequence() 확장 함수를 만들 수 있다. 이 템플릿으로 Sequence 뿐만 아니라 Iterables, ArraysOfObjects, ArraysOfPrimitives에도 생성된다. 이는 코드 내부의 include 함수를 보면 알 수 있다. include 함수 대신에 includeDefault() 함수도 있는데 이는 Templates.kt에서 정의를 찾을 수 있다. 내부 코드를 보면 Family 클래스에 대해서도 궁금해할 수 있는데 CommonTypes.kt에서 해당 클래스를 확인할 수 있다.

 

_Sequence.kt에는 Sequence 템플릿에 없는 확장 함수도 있다. 이 역시 include 함수를 통해 다른 템플릿에서 생성되는 것이다. 예를 들면 Numeric.kt 템플릿에는 sum 확장 함수가 들어가 있는데 이 템플릿에의해 자동생성되어 _Sequence.kt에서도 sum 확장함수를 볼 수 있다.

 

/**
 * Numeric.kt 템플릿
 */
val f_sum = fn("sum()") {
    listOf(Iterables, Sequences, ArraysOfObjects).forEach { include(it, summablePrimitives) }
    include(ArraysOfPrimitives, numericPrimitivesDefaultOrder)
    include(ArraysOfUnsigned)
} builder {
    // ... 생략 ...
}

/**
 * _Sequences.kt
 */
@kotlin.jvm.JvmName("sumOfShort")
public fun Sequence<Short>.sum(): Int {
    // ... 생략 ...
}

@kotlin.jvm.JvmName("sumOfInt")
public fun Sequence<Int>.sum(): Int {
    // ... 생략 ...
}

// 추가로 sum 확장함수가 더 있음

 

 

 

결론

많은 컬렉션에 자동으로 생성할 수 있는 확장 함수는 언더바가 붙은 클래스 내부에 정의되고, 템플릿 형태로 자동생성하기 어려운 구조의 확장함수는 언더바가 없는 클래스 내부에 정의된다.

 

잠깐 궁금했던건데 이렇게나 오래 학습하게될 줄이야...

728x90
반응형