이번 장에서는 제네릭에 대한 자세한 설명과 함께, 코틀린에서 새롭게 도입된 컨셉을 살펴볼 것이다. reified type parameter와 declaration-site variance등의 새로운 개념에 대해서 학습해보고자 한다.

#제네릭 타입 파라미터

제네릭은 타입 파라미터를 정의내릴 수 있도록 도와준다. 인스턴스가 생성되는 순간, 타입 파라미터는 타입 인수로 변경된다. 예를 들어, Map<K, V>라고 선언한 후 Map<String, Person>와 같이 특정한 인자로 변경할 수 있다.

//string을 인자로 넘겨주기 때문에 자동으로 List<String>
val authors = listOf("Dmitry", "Svetlana")
//빈 리스트를 생성하기 때문에 List<String>라는 것을 명시적으로 표시
val readers: MutableList<String> = mutableListOf()
val readers = mutableListOf<String>()

<aside> 📌 코틀린은 반드시 제네릭 타입의 타입 인자를 정의해야한다:

자바의 경우 최초에는 제네릭이라는 개념이 없었고, 이후 업데이트를 통해 1.5에서 처음 제네릭이 나왔다. 이로 인해 자바에서는 컬렉션을 선언할 때 원소 타입을 지정하지 않아도 컬렉션을 생성할 수 있었다. 그러나 코틀린은 처음부터 제네릭을 도입했기 때문에 반드시 제네릭 타입의 인자를 정의해주어야(프로그래머가 정의하든, 타입 추론에 의해 정의되든) 사용할 수 있다.

</aside>

#제네릭 함수와 프로퍼티

리스트를 사용하는 함수이지만 그 어떤 타입의 리스트인 간에 상관없이 사용할 수 있게 만들고 싶다면 제너릭 함수를 만들어야 한다. 제너릭 함수는 자신만의 타입 파라미터를 가지고 있다. 이러한 타입 파라미터는 함수가 깨어날 때 특정한 인자로 변경되어야 한다.

Untitled

위의 slice 함수를 호출할 때는 타입 인자를 명시적으로 표현해야 한다. 그러나 많은 경우 컴파일러가 이를 추론하기 때문에 그럴 필요가 없다. 예를 들어, letters.slice(10..13)와 같이 호출한다면 컴파일러는 타입 파라미터 T가 Char라는 것을 자동으로 추론한다. 즉, letters.slice<Char>(0..2)처럼 직접 명시해 줄 필요가 없다.

val authors = listOf("Dmitry", "Svetlana")
val readers = mutableListOf<String>(/* ... */)
fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>
>>> readers.filter { it !in authors }

위의 예시에서, itString이 된다. 컴파일러는 이를 추론하여 제너릭 타입 TString이 된다는 것을 알아낼 수 있다.

<aside> 📌 non-extension property에 대해 제너릭 선언 불가

</aside>

#제너릭 클래스 선언

코틀린은 자바와 마찬가지로 꺽쇠 기호<>를 사용하여 클래스나 인터페이스를 제네릭하게 만들 수 있다. 이렇게 정의내린 후에는 타입 파라미터를 클래스의 본문에서 사용할 수 있다. 예시로 코틀린의 List 인터페이스를 살펴보자.

interface List<T> { //타입 파라미터 T를 정의내림
	operator fun get(index: Int): T //T는 레귤러 타입과 마찬가지로 인터페이스나 클래스에서 사용 가능
	// ...
}

기본적으로, 코틀린의 제네릭은 자바와 매우 비슷하다. 이제부터는 다른 점을 살펴보도록 하자.

#타입 파라미터 제약

type paramter constraint는 클래스나 함수에 사용할 수 있는 타입 인자를 제한하는 기능이다. 예를 들어, 리스트에 있는 원소를 모두 더하는 함수가 있다고 가정하자. 이 함수는 List<Int>나 List<Double>에 사용될 수 있지만, List<String>에는 사용 불가능하다. 이를 표현하기 위해서 타입 파라미터는 숫자만 될 수 있다는 것을 명시해야 한다.

어떤 타입을 제네릭 타입의 타입 파라미터에 대한 **상한(upper bound)**으로 지정하면 그 제네릭 타입을 인스턴화할 때 사용하는 타입 인자는 반드시 그 상한 타입이거나 그 상한 타입의 하위 타입이어야 한다.

Untitled