Kotlin/Effective Kotlin 요약

24장 - 제네릭 타입과 variance 한정자를 활용하라

Junuuu 2023. 3. 7. 00:01
반응형

불공변성(invariant)이란?

제네릭 타입으로 만들어지는 타입이 서로 관련성이 없다는 의미입니다.

 

예를 들어 다음 제네릭들은 서로 어떠한 관련성도 갖지 않습니다.

- Cup<Int>

- Cup<Number>

- Cup<Any>

- Cup<Nothing>

 

공변성(covariant)이란?

어떤 관련성을 원할 경우 out 또는 in이라는 variance 한정자를 붙일 수 있습니다.

out은 타입 파라미터를 공변성으로 만듭니다.

A가 B의 서브타입일때 Cup<A>가 Cup<B>의 서브타입이라는 의미입니다.

 

in은 반대 의미입니다.

타입 파라미터를 contravariant(반변성)으로 만듭니다.

A가 B의 서브타입일때 Cup<A>가 Cup<B>의 슈퍼타입이라는 의미입니다.

 

코틀린에서 함수와 variance

코틀린 함수 타입의 모든 파라미터 타입은 contravariant입니다.

또한 모든 리턴 타입은 covariant입니다.

 

(Int) -> Any 타입의 함수는 다음과 같이도 동작합니다.

(Int) -> Number

(Number) -> Any

(Number) -> Number

(Number) -> Int

 

variance 한정자의 안정성

자바의 배열은 convariant입니다.

이로 인해 컴파일 중에 아무런 문제는 없지만, 런타임 오류가 발생합니다.

Integer[] numbers = {1, 4, 2, 1};
Object[] objects = numbers;
Objects[2] = "B"; //런타임오류: ArrayStoreException

numbers를 Object[]로 캐스팅해도 내부에서는 여전히 Integer를 사용하고 있습니다.

이런 배열에 String 타입의 값을 할당하면 오류가 발생합니다.

 

코틀린은 이런 결함을 해결하기 위해 invariant로 만들었습니다.

 

정리

코틀린의 타입 아규먼트의 관계에 제약을 걸 수 있는 굉장히 강력한 제너릭 기능을 제공합니다.

 

- 타입 파라미터의 기본적인 variance의 동작은 invariant입니다. 만약 Cup<T>라고 하면, 타입 파라미터 T는 invariant입니다. A가 B의 서브타입이라고 할 때 Cup<A>와 Cup<B>는 아무런 관계를 갖지 않습니다.

 

- out 한정자는 타입 파라미터를 convariant하게 만듭니다. 만약 Cup<T>라고 하면, A가 B의 서브타입이라고 할 때, Cup<A>는 Cup<B>의 서브타입이 됩니다.

 

- in 한정자는 out과 반대로 Cup<B>가 Cup<A>의 슈퍼타입이 됩니다.

 

- 코틀린에서 List와 Set의 타입 파라미터는 covariant(out 한정자)입니다.

 

- 함수 타입의 파라미터 타입은 contravariant(in 한정자)입니다. 그리고 리턴 타입은 covariant(out 한정자)입니다.