나는 안드로이드 개발을 하면서 공변성과 반공변성을 들어본 적이 종종 있다.
하지만 어떤 의미를 갖는 지는 제대로 파악해본 적이 없었다. 하지만 개발을 하는 입장에서 알고가면 어떤 의도로 이걸 사용하는 지 파악할 수 있을 것 같아서 좀 더 공부해보기로 했다.
공변성을 설명하기 위해서 간단한 예시를 들어보자.
String타입이 Any의 하위 타입인 것은 모두 알것이다.
그럼 List<String>도 List<Any>의 하위타입일까?
정답은 yes이다. 그게 바로 공변성이다.
A타입이 B타입의 하위타입일 때
Base<A>
가 Base<B>
의 하위타입이 성립되는 경우이다.
간단한 예시를 들어보자.
Animal이라는 상속 가능한 클래스가 있다고 예시를 들면
Pig, Dog는 모두 Animal에 하위타입이다.
val pig = Pig()
val animal : Animal = pig
그냥 단순히 이렇게 구현하면 될 것이다.
하지만 타입이 2개이상부터는 자연적으로 업캐스팅이 되지않는다.
open class Animal()
class Pig : Animal()
class Dog : Animal()
class Box<out T>(val value : T)
fun main() {
val boxPig : Box<Pig> = Box(Pig())
val animal : Box<Animal> = boxPig
}
2개이상부터는 out T
라고 명시를 해줘야 공변성이 성립이 된다.
그래서 위에 코드가 정상적으로 컴파일이 되는 것이다.
공변성에는 아주 중요한 규칙이 있는데,
바로 생산자로만 사용할 것이다. 즉 파라미터로 받아서 소비하는 형태는 금지한다.
예를 들면
class Box<out T>(val value : T) {
fun consumer(v : T) {
// 대충 v를 소비하는 코드
}
}
해당 코드는 컴파일 단계에서
Type parameter 'T' is declared as 'out' but occurs in 'in' position in type 'T (of class Box)'.
해당 오류가 뜨는 걸 볼 수 있다. T의 위치가 in T 위치처럼 쓰였다는 것이다. 즉, 생성자로만 쓰어야 되는데 소비자로 쓰였다는 뜻이다.
class Box<out T>(val value : T) {
fun producer() : T {
// 대충 생산하는 코드
}
}
타입을 Pig로 받고 Dog를 넣어서 소비를 해버리면 타입 에러가 나기때문에
공변성을 사용할 때는 생산자로만 사용해야 된다.
위에 공변성을 보면서 대충 감은 왔을 것이다.
공변성의 반대라고 생각하면 편하다.
즉 하위타입과 상위타입에 관계가 반대로 되는 것이다.
아까와 같은 예시를 보자
open class Animal()
class Pig : Animal()
class Dog : Animal()
class Box<in T>()
fun main() {
val animalBox = Box<Animal>()
val pigBox : Box<Pig> = animalBox
}
얼핏 보면 조금 의아할 것 이다. pig가 하위타입인데 pig라는 타입에 어떻게 상위타입인 Animal이 들어갈 수 있을까? 그건 바로 in T
라는 키워드 덕분이다.
공변성은 생성자로만 사용 할 수 있다고 배웠다. 역시나 반대로 반공변성은 소비자로만 사용할 수 있다.
open class Animal()
class Pig : Animal()
class Dog : Animal()
class Box<in T>() {
fun consumer(value : T) {
println("$value")
}
}
fun main() {
val animalBox = Box<Animal>()
val pigBox : Box<Pig> = animalBox
pigBox.consumer(Pig())
// pigBox.consumer(Dog()) x!!! 컴파일 에러
}
이렇게 사용할 수 있다. 특정 타입을 안전하게 받아서 소비할 수 있도록 해주는 역할인 것 같다.
공변성은 하위 타입을 상위타입으로 보게끔하여 생산자로써 안전하게 타입캐스팅을 해주는 역할이고
반공변성은 하위타입에서의 소비를 상위타입이 안전하게 처리할 수 있기때문에 소비함에 있어서 안전하게 처리해주는 역할인 것 같다.