Kotlin에서 List, Map, Set같은 Collections 클래스를 사용할 때, List<String> 이런식으로 제네릭 타입을 지정할 수 있다.

제네릭을 사용하면 타입 안정성을 얻을 수 있다.
하지만 제네릭으로 지정한 타입 이외에도 좀 더 유연하게 사용하고 싶을 수 있다.
그래서 코틀린에서는 공변성과 반공변성을 허용한다.

  • 공변성이란, 파라미터 타입 T의 자식클래스를 T의 자리에 사용할 수 있도록 허용하는 것이다.
  • 반공변성이란, 파라미터 타입 T의 부모클래스를 T의 자리에 사용할 수 있도록 허용하는 것이다.
open class Fruit
class Banana : Fruit()
class Orange : Fruit()

fun receiveFruits(fruits: Array<Fruit>) {
    println("${fruits.size}")
}

receiveFruits함수 안에서는 fruits의 크기만 출력하고 있다.

하지만, 파라미터로 Array<Banana>, Array<Orange>를 전달할 수 없다.

왜냐하면 함수 안에서 fruits.add(Orange()) 을 한다고 할 때, Array<Banana>를 전달했다면 타입 에러가 나기 때문이다.

하지만 fun receiveFruits(fruits: List<Fruit>)라면 어떨까? (Array -> List)

된다. 왜?

  • Array는 Mutable 하지만, List는 Immutable하기 때문이다.
    • 따라서 함수 내부에서 fruits.add(Orange()) 이런 연산 자체를 하지 못한다.
  • 각 타입이 정의되는 곳을 보면 Array는 Array<T>, List는 List<out T>로 정의한다.

out 에 대해 알아보자.

out은 공변성을 사용할 수 있게 해주는 키워드이다. 즉, 파라미터 타입으로 T의 자식타입을 사용할 수 있게 해준다.
어떻게 그렇게 하는 걸까?

fun copyFromTo(from: Array<out T>, to: Array<T>) 처럼 선언 했을 때, 함수 안에서 from에 "쓰기" 작업을 하지 않는다는 보장이 있어야 한다. "쓰기"작업이 있는지 없는지는 컴파일러가 판단해 있다면 컴파일 시간에 오류를 준다.

이렇게 Array라는 제네릭 클래스를 정의한 곳이 아닌 사용하는 쪽에서 out으로 공변성을 이용하는 것을 사용처 가변성 또는 타입 프로젝션이라고 부른다.

List와 같이 제네릭 클래스를 정의할 때 out으로 공변서을 이용한다면 선언처 가변성이라고 한다.

예시

fun copyFromTo(from: Array<Fruit>, to: Array<Fruit>) {
    for (i in 0 until from.size) {
        to[i] = from[i]
    }
}

val fruits = Array<Fruit>(3) { _ -> Fruit() }
val bananas = Array<Banana>(3) { _ -> Banana() }
copyFromTo(bananas, fruits)

이 경우 from은 "읽기"만 하고있어 로직 상으로는 전혀 문제가 없지만, Array<Banana>Array<Orange>타입이 들어갈 수 없다.
Fruit의 자식 클래스도 전달할 수 있게 만드려면 from: Array<out Fruit>으로 선언하면 된다.

in; 반공변성은 무엇인가.

공변성과 반대로, 파라미터 타입에 부모클래스를 사용하고 싶을 수 있다.

위의 예시에서 to위치에 Fruit의 부모클래스를 받고 싶다면 in 키워드를 사용하면 된다.

to: Array<in Fruit> 로 선언하면 Fruit의 부모타입도 받을 수 있다.

'Programming > 언어' 카테고리의 다른 글

[Kotlin] Delegation  (0) 2023.03.01
[Kotlin] object, companion object  (0) 2023.02.26
[Kotlin] Data Class  (0) 2023.02.15
[Kotlin] Collection의 View  (0) 2023.02.13
[Kotlin] Unit, Any, Nothing 클래스  (0) 2023.02.12

+ Recent posts