Table of contents
Open Table of contents
변성
변성(variance)는 List<String>
와 List<Any>
와 같이 기저 타입이 같고 타입 인자가 다른 경우에 서로 어떤 관계를 가지는지를 설명합니다.
즉, 변성은 서브타입 관계가 있는지 설명하는 개념입니다.
변성을 잘 활용하면 사용에 불편하지 않으면서 타입 안정성을 보장하는 API를 만들 수 있습니다.
제네릭 클래스가 아닌 클래스에서는 클래스 이름을 바로 사용하여 타입을 지정할 수 있습니다.
class Box(val value: Int)
val box: Box = Box(1) // Box 타입
하지만 제네릭 클래스에서는 구체적인 타입 인자를 지정해야 합니다.
예를 들어 List 클래스는 제네릭 클래스이고 List<String>
과 List<Int>
와 같이 타입 인자를 지정하여 타입을 만들어야 합니다.
class Box<T>(val value: T)
val box: Box<Int> = Box(1) // Box<Int> 타입
공변성(covariance)
공변성은 서브타입 관계가 유지되는 것을 의미합니다.
예를 들어, List
가 공변적이라면 List<String>
은 List<Any>
의 서브타입이 됩니다.
코틀린에서는 out
키워드를 사용하여 공변성을 지정할 수 있습니다.
open class Animal {
fun feed() {
println("feed")
}
}
class Cat : Animal() {
fun meow() {
println("meow")
}
}
class Dog : Animal() {
fun bark() {
println("bark")
}
}
class Cage<out T : Animal>(val animal: T)
fun main() {
val catCage: Cage<Cat> = Cage(Cat())
val dogCage: Cage<Dog> = Cage(Dog())
val animalCage: Cage<Animal> = catCage
animalCage.animal.feed()
}
Cage
클래스는 T
타입의 동물을 담는 클래스입니다.
out
키워드가 사용된 타입은 메소드 내에서 T
타입의 값을 생산할 수 있습니다.
따라서 Cage<Cat>
은 Cage<Animal>
의 서브타입이 됩니다.
out
키워드는 또한 반환 타입이나 프로퍼티의 타입에서만 메소드 사용을 허용하여 하위 타입관계의 안전성을 보장합니다.
반공변성(contravariance)
반공변성은 슈퍼타입 관계가 유지되는 것을 의미하며 공변성의 반대 개념입니다.
코틀린에서는 in
키워드를 사용하여 반공변성을 지정할 수 있습니다.
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0은 Number의 서브타입입니다.
}
fun main() {
val x = object : Comparable<Number> {
override fun compareTo(other: Number): Int = 0
}
demo(x)
}
Comparable
인터페이스는 T
타입의 값을 비교하는 compareTo
함수를 가지고 있습니다.
in
키워드가 사용된 타입은 메소드 내에서 T
타입의 값을 소비할 수 있습니다.
공변성 | 반공변성 | 무공변성 |
---|---|---|
List<out T> | Comparator<in T> | MutableList<T> |
타입 인자의 하위 타입 관계가 제네릭 타입에서도 유지된다. | 타입 인자의 하위 타입 관계가 제네릭 타입에서는 뒤집힌다. | 하위 타입 관계가 성립하지 않는다. |
List | Comparator | |
T 를 아웃 위치에서만 사용할 수 있다. | T 를 인 위치에서만 사용할 수 있다. | T 를 아무 위치에서나 사용할 수 있다. |
참고
- Variance
- 코틀린 인 액션