Kotlin
[Kotlin] 11.3 변성은 인자를 함수에 넘겨도 안전한지 판단하게 해준다
mygomii
2025. 5. 25. 20:17
반응형
- 변성 개념은 List<String>과 List<Any> 같이 기저 타입이 같고 타입 인자가 다른 여러 타입이 어떤 관계가 있는지 설명하는 개념
11.3.1 변성은 인자를 함수에 넘겨도 안전한지 판단하게 해준다.
- 코틀린의 제네릭은 기본적으로 공변(covariant)하지 않음
- 예를 들어 List<String>은 List<Any>의 하위 타입이 아님
- 하지만 우리가 알고 있는 일반적인 타입 상속 관계(예: String → Any)와 달리, 제네릭 타입은 타입 인자가 달라지면 무관한 타입으로 간주됨
val strings = listOf("a", "b", "c")
printContents(strings) // 컴파일 오류 발생!
- 이유는 List<Any>와 List<String>은 별개의 타입으로 간주되므로, 타입 안전성 보장을 위해 허용되지 않음.
11.3.2 클래스, 타입, 하위 타입
- 클래스와 타입은 다름 코틀린에서는 한 클래스가 여러 타입을 가질 수 있음
- 특히 제네릭 클래스의 경우, 타입 인자에 따라 서로 다른 “타입”으로 간주됨
class Box<T>(val value: T)
- Box<Int> , Box<String> , Box<Any>
11.3.3 공변성은 하위 타입 관계를 유지한다.
- 공변성(covariance)
- 하위 타입 관계가 유지되는 것을 의미함.
- 즉, String이 Any의 하위 타입이라면, Producer<String>이 Producer<Any>의 하위 타입이 되도록 허용하는 것.
interface Producer<out T> {
fun produce(): T
}
- out T는 *T를 반환(출력)*하는 데만 사용된다는 뜻.
- 코틀린 컴파일러는 T가 출력 전용(produced-only) 으로만 쓰인다는 걸 보장할 수 있으면 공변성을 허용함
- 이로 인해 하위 타입 관계가 유지될 수 있음
- 왜 공변성이 필요한가?
- 타입 인자가 읽기 전용(read-only) 으로 사용될 때는 공변성 적용이 완전 안전
- 따라서 List<out T>, Sequence<out T>처럼 코틀린의 표준 라이브러리에서도 적극적으로 사용됨
11.3.4 반공변성은 하위 타입 관계를 뒤집는다.
- 반공변성(Contravariance)이란?
- 일반적으로 String은 Any의 하위 타입이야.
- 그런데 Consumer<Any>가 Consumer<String>의 하위 타입이 되는 걸 반공변성이라 한다.
- 즉, 타입 관계가 뒤집힌다는 뜻.
- 왜 타입 관계가 뒤집힐까?
- Consumer<T>는 값을 받기만(consumes) 하는 역할이기 때문에
- → 더 일반적인 타입이어야 안전함
- 예를 들어 Consumer<Any>는 모든 타입을 받을 수 있으니, Consumer<String>이 필요한 곳에 넣어도 문제 없이 동작할 수 있어.
interface Consumer<in T> {
fun consume(item: T)
}
- in T는 T를 입력 전용(consume-only) 으로 사용하겠다는 의미
- 반공변성 적용을 통해 타입 관계를 뒤집을 수 있게 허용함
구분키워드방향성설명
공변성 | out | 하위 → 상위 | 읽기 전용: Producer<String> → Producer<Any> |
반공변성 | in | 상위 → 하위 | 쓰기 전용: Consumer<Any> → Consumer<String> |
무변성 | 없음 | 관계 없음 | Box<String> ≠ Box<Any> |
반응형