코틀린 표준 라이브러리는 컬렉션을 위한 여러 제네릭 함수를 제공한다. 제네릭 확장 함수를 쓰려면 수신 객체 앞에 제네릭 명세(괄호로 둘러싼 타입 파라미터 목록)를 위치시켜야 한다. 예를 들어 first()나 firstOrNull() 정의를 살펴보자.
IntroGenerics/GenericListExtensions.kt
package introgenerics
import atomictest.eq
fun <T> List<T>.first(): T {
if (isEmpty())
throw NoSuchElementException("Empty List")
return this[0]
}
fun <T> List<T>.firstOrNull(): T? =
if (isEmpty()) null else this[0]
fun main() {
listOf(1, 2, 3).first() eq 1
val i: Int? = // [1]
listOf(1, 2, 3).firstOrNull()
i eq 1
val s: String? = // [2]
listOf<String>().firstOrNull()
s eq null
}
first()와 firstOrNull()은 모든 List에 대해 작동할 수 있다. T 타입의 값을 반환하기 위해서는 제네릭 함수로 이 두 함수를 정의해야만 한다.
firstOrNull()에서 어떻게 반환 타입을 널이 될 수 있는 타입으로 명시했는지 살펴보라.
• [1] List<Int>에 대해 firstOrNull()을 호출하면 Int?가 반환되는 모습을 보여준다.
• [2] List<String>에 대해 같은 함수를 호출해서 String?를 받는다.
코틀린은 [1]과 [2]에서 모두 식별자 타입에 ?를 요구한다. 시험 삼아 ?를 제거하고 어떤 오류 메시지가 표시되는지 살펴보라.