TIL

아이템 24 제네릭 타입과 variance 한정자를 활용하라

class Cup<T>
open class Dog
class Puppy: Dog()

class Cup<out T>

val b: Cup<Dog> = Cup<Puppy> // OK
val a: Cup<Puppy> = Cup<Dog>() // error
class Cup<in T>

val b: Cup<Dog> = Cup<Puppy> // error
val a: Cup<Puppy> = Cup<Dog>() // ok

함수 타입

fun printProcessedNumber(transaction: (Int) -> Any) {
	print(transaction(42))
}

val intToDouble: (Int) -> Number = { it.toDouble() }
val numberAsText: (Number) -> Any = { it.toShort() }
val identity: (Number) -> Number = { it }

printProcessedNumber(intToDouble)
printProcessedNumber(numberAsText)
printProcessedNumber(identity)

variance 한정자의 안정성

자바 배열과 코틀린 배열

Integer[] numbers = {1, 4, 2, 1};
Object[] objects = numbers; // convariant이기에 서브타입 취급하여 할당 가능
objects[2] = "B"; // 런타임 오류: ArrayStoreException

convariant(out) 타입 파라미터와 public in 위치

class Box<out T> {
	private var value: T? = null
	
	fun set(value: T) { // 코틀린에서 컴파일 오류 발생!
		this.value = value
	}
}
val dogHouse = Box<Dog>()
val box: Box<Any> = dogHouse // Puppy는 Any의 서브 타입이기에 할당 가능 (convariant)
box.set("Some string") // ?
box.set(42) // ?
class Box<out T> {
	private var value: T? = null
	
	private fun set(value: T) {
		this.value = value
	}
}

contravariant(in) 타입 파라미터와 public out 위치

class Box<in T> {
	val value: T // 에러
}

val noSpot: Box<Nothing> = Box<Car>(Car())
val boat: Nothing = noSpot.value // Car를 위한 공간이다
class Box<in T> {
	private val value: T
}

variance 한정자의 위치

선언 부분

// 선언 쪽
class Box<out T>(val value: T)

val boxStr: Box<String> Box("Str")
val boxAny: Box<Any> boxStr

클래스와 인터페이스를 활용하는 부분

class Box<T>(val value: T)

val boxStr: Box<String> = Box("Str")

// 사용하는 쪽
val boxAny: Box<out Any> = boxStr

정리

코틀린은 타입 아규먼트 관계에 제약을 걸 수 있는 강력한 제네릭 타입을 제공한다.

코틀린에서는