Kotlin

kotlin generic variance

voider 2022. 2. 27. 22:52

다음과 같은 제네릭 클래스가 있다.

class Cup<T>

위 코드에서 타입 파라미터 T 는 variance 한정자(out 또는 in )가 없다. 이럴 때는 기본적으로 invariant(불공변성)이다. invariant라는 건 제네릭 타입으로 만들어지는 타입들이 서로 관련성이 없다는 뜻. 말하자면 Cup<String> 과 Cup<Hoon> , Cup<Sal> 은 전혀 관계없다.

불공변성 관계

fun main() {
	val anys: Cup<Any> = Cup<Int> //Error: Type mistach
	val nothings: Cup<Nothing> = cup<Int>() // Error
}

만약 어떤 관련성을 원한다면 out 또는 in 이라는 variance 한정자를 붙인다. out 은 타입 파라미터를 covariant(공변성)로 만든다. 이는 A가 B의 서브타입일 때 Cup<A> 가 Cup<B> 의 서브타입이라는 의미다.

공변성 관계

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

fun main() {
	val b: Cup<Dog> = Cup<Puppy> // OK
	val a: Cup<Puppy> = Cup<Dog> // Dog은 Puppy의 서브타입이 아니므로 Error

	val anys: Cup<Any> = Cup<Int>() //out T로 선언해서 이번엔 OK
	val nothings: Cup<Nothings> = Cup<Int>() // Error
}

in 한정자는 반대 의미다. 타입 파라미터를 contravariant(반변성)으로 반든다. 이것은 A가 B의 서브타입일 때, Cup<A> 가 Cup<B> 의 슈퍼타입이라는 것을 의미한다.

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

fun main() {
    val b: Cup<Dog> = Cup<Puppy>() // type mismatch
    val a: Cup<Puppy> = Cup<Dog>() // OK

    val anys: Cup<Any> = Cup<Int>() // type mismatch
    val nothins: Cup<Nothing> = Cup<Int>() // OK
}

varience 한정자를 그림으로 나타내면 다음과 같다

참고: 이펙티브 코틀린 아이템 24