지네릭
자바와 마찬가지로, 코틀린 클래스는 타입 파라미터를 가질 수 있다
class Box<t>(t: T) {
var value = t
}
일반적으로 지네릭 클래스의 인스턴스를 생성할 때 타입 인자를 전달해야 한다
val box: Box<Int> = Box<Int>(1)
하지만 파라미터를 추론할 수 있다면 타입 인자를 생략할 수 있다
val box = Box(1) // 1은 Int 타입이므로 컴파일러는 Box<Int>를 말한다는 것을 알아낼 수 있다
변성
코틀린은 자바의 와일드카드 타입 대신 선언 위치 변성과 타입 프로젝션을 제공
선언 위치 변성
지네릭 인터페이스 Source
// Java
interface Source<T> {
T nextT();
}
Source
// 자바
void demo(Source<String> strs) {
Source<Object> objects = strs; // 자바는 허용하지 않음
// ...
}
이를 고치려면 Source<? extends Object> 타입의 객체를 선언해야 하는데 이는 의미가 없다
왜냐면 전처럼 변수에 대해 동일 메서드를 호출할 수 있고 이득없이 타입만 더 복잡해지기 때문이다
하지만 컴파일러는 이를 모른다
코틀린에서는 이를 컴파일러에 알려주는 방법을 제공. 이를 선언 위치 변성이라고 부른다
Source의 타입 파라미터 T에 대해 Source
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 괜찮다. T가 out 파라미터이기 때문이다
// ...
}
클래스 C의 타입 파라미터 T를 out으로 선언하면, C의 멤버에서 out위치에만 T가 위치할 수 있지만
리턴에서 C
out수식어를 애노테이션이라 부른다
out외에 코틀린은 추가 공변 애노테이션 in을 제공한다. 이는 파라미터를 반공변으로 만든다
반공변 파라미터는 오직 소비만 할 수 있고 생산할 수는 없다
abstract class Comparable<in T> {
abstract fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0은 Double타입인데 이는 Number의 하위타입이다
// 따라서 Comparable<Double> 타입의 변수에 x를 할당할 수 있다
val y: Comparable<Double>= x // OK
지네릭 함수
클래스뿐만 아니라 함수도 타입 파라미터를 가질 수 있다. 함수 이름 앞에 타입 파라미터를 위치시킴
fun <T> singletonList(item: T): List<T> {
// ...
}
fun <T> T.basicToString() : String { // 확장 함수
// ...
}
지네릭 함수를 호출하려면 호출 위치에서 함수 이름 뒤에 타입 인자를 지정한다
val l = singletonList<Int>(1)