[Kotlin] 공변성? 반공변성? 그게 뭔데

우발자·2025년 8월 31일
1
post-thumbnail

나는 안드로이드 개발을 하면서 공변성과 반공변성을 들어본 적이 종종 있다.
하지만 어떤 의미를 갖는 지는 제대로 파악해본 적이 없었다. 하지만 개발을 하는 입장에서 알고가면 어떤 의도로 이걸 사용하는 지 파악할 수 있을 것 같아서 좀 더 공부해보기로 했다.


공변성이란?

공변성을 설명하기 위해서 간단한 예시를 들어보자.

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!!! 컴파일 에러
}

이렇게 사용할 수 있다. 특정 타입을 안전하게 받아서 소비할 수 있도록 해주는 역할인 것 같다.


요약하자면,

공변성은 하위 타입을 상위타입으로 보게끔하여 생산자로써 안전하게 타입캐스팅을 해주는 역할이고
반공변성은 하위타입에서의 소비를 상위타입이 안전하게 처리할 수 있기때문에 소비함에 있어서 안전하게 처리해주는 역할인 것 같다.

profile
나는 안드로이드 개발자다.

0개의 댓글