제네릭 타입과 variance 한정자를 활용하라 #
class Cup<T>
위 코드에서 (타입 파라미터)T
는 variacne 한정자(out 또는 in)가 없으므로, invariant(불공변성)이다.
invariant 는 제네릭 타입으로 만들어지는 타입들이 서로 아무 관계가 없다는 것을 의미한다. (e.g., Cup<Coffee>
, Cup<Int>
, Cup<Any>
, Cup<Nothing>
)
만약 제네릭 타입으로 만들어지는 타입에 관련성을 원한다면, variance 한정자(out 또는 in)를 사용한다.
variance | 설명 |
---|---|
out | covariance (공변성) |
in | contravariant(반변성) |
out (covariance) #
class Cup<out T>
open class Dog
class Puppy: Dog()
fun main(args: Array<String>) {
val a: Cup<Dog> = Cup<Puppy>() // OK
val b: Cup<Puppy> = Cup<Dog>() // ERROR
}
in (contravariant) #
class Cup<in T>
open class Dog
class Puppy: Dog()
fun main(args: Array<String>) {
val a: Cup<Dog> = Cup<Puppy>() // ERROR
val b: Cup<Puppy> = Cup<Dog>() // OK
}
variant, invariant 정리 #
함수 타입 #
함수타입은 파라미터 타입과 리턴 타입에 따라 서로 어떤 관계를 갖는다.
예를 들어, 다음 (Int) -> Any
와 같은 함수 타입이 있다고 가정하자.
fun printProcessedNumber(transition: (Int) -> Any) {
print(transition(42))
}
(Int) -> Any 타입의 함수는 아래 형태로도 동작한다.
- (Int) -> Number
- (Number) -> Any
- (Number) -> Number
- (Number) -> Int
코틀린 함수 타입의 모든 파라미터 타입(위 예시에서 Int
)은 contravariant
이다.
코틀린 함수 타입의 모든 리턴 타입(위 예시에서 Any
)은 covariant
이다.
fun main(args: Array<String>) {
val numberToInt: (Number) -> Int = { it.toInt() }
val numberHash: (Any) -> Number = { it.hashCode() }
val intToInt: (Int) -> Int = { it }
printProcessedNumber(numberToInt) // OK
printProcessedNumber(numberHash) // OK
printProcessedNumber(intToInt) // ERROR
}
fun printProcessedNumber(transition: (Number) -> Any) {
println(transition(42))
}
함수 타입을 사용할 때는 위처럼 자동으로 variance 한정자가 사용된다. :star: :star:
" 코틀린에서 자주 사용되는 것으로는 covariant(out 한정자)를 가진 List 가 있습니다. 이는 variance 한정자가 붙지 않은 MutableList와 다릅니다. 왜 MutableList보다 List를 더 많이 사용하는지, 그리고 어떤 부분이 다른 것인지는 variance 한정자의 안정성과 관련된 내용을 이해하면 알 수 있습니다. “
variance 한정자의 안정성 #
자바의 배열은 covariant(out) 이다.
많은 출처에 따르면, 이렇게 만들어진 이유가 배열을 기반으로 제네릭 연산자는 정렬 함수 등을 만들기 위해서라고 한다.
그런데 covariant 속성으로 인한 결함이 있다.
아래 코드는 컴파일 중에는 아무 문제가 없지만, 런타임 시 오류가 발생한다.
Integer[] numbers = {1, 4, 2, 1};
Object[] objects = numbers;
objects[2] = "B" // ERROR
코틀린은 이 결함을 해결하기 위해 Array(IntArray, CharArray 등)를 invariant 로 만들었다.
따라서, Array<Int>
를 Array<Any>
등으로 바꿀 수 없다.
예시를 봐야 할듯.
이 아이템은 좀 찾아볼 것