[코틀린 완전정복] 공변성, 반공변성, 무변성

Dohyeon Ko·2021년 9월 12일
5

코틀린

목록 보기
4/7
post-thumbnail
post-custom-banner

가변성

변할 수 있다? 가변성이 뭐에요? 🙄

코틀린 에서의 가변성은 형식 매개변수가 클래스 계층에 영향을 주는 것을 말한다. 예를 들어 형식 A의 값이 필요한 모든 클래스에 형식 B의 값을 넣어도 문제가 없다면 B는 A의 하위 형식이 된다. 아래 코드를 보자.

val integer : Int = 1
val number : Number = integer // Number는 상위 자료형, Int는 하위 자료형

Int형으로 선언한 integer 변수가 Number형으로 선언한 number 변수에 할당해도 아무 문제가 없는 것을 볼 수 있다. 이게 바로 가변성이다. 또 다른 예로는 Int?와 Int가 있다. Int?는 null도 포함하므로 Int?가 Int의 상위 자료형이 된다.

가변성의 3가지 유형

기본적으로 제네릭에서는 클래스 간의 상위와 하위의 개념이 없어서 서로 무관하다. 만약 상위와 하위에 따른 형식을 주려면 어떻게 해야할까? 이때 변성 이라는 새로운 개념이 적용된다. 가변성에는 공변성, 반공변성, 무변성 3가지 유형이 존재하는데 천천히 알아보도록 하자.

갑자기 지구과학 수업이 된 거 같은데요? 😥

우선 Int형이 Number형의 하위 자료형인 것은 명확하다. 이 때 Class Box< Out T >로 정의하면 Class Box< Int >는 Class Box< Number >의 하위 자료형이 된다. 그냥 그대로 이해하면 될 것이다. 공변성은 형식 매개변수의 상, 하위 개념을 그대로 가져온다는 느낌이다.

  • 공변성 - A가 B의 하위 자료형이면 Class< A >는 Class< B >의 하위 자료형이다.

    생산자 입장의 Out 성질

반공변성공변성과 반대의 개념이다. 기본적으로 Int형이 Number형의 하위 자료형이라는 사실은 명백하고, Class Box< In T > 로 정의하면 Class Box< Number >가 Class Box< Int >의 하위 자료형이 된다.

  • 반공변성 - A가 B의 하위 자료형이면 Class< B >는 Class< A >의 하위 자료형이다.

    소비자 입장의 In 성질

무변성두 클래스 사이에 아무런 관계도 없다는 개념이다.

  • 무변성 - Class< A >와 Class< B >는 아무 관계가 없다.

    생산자 + 소비자

공변성과 반공변성이 조금 헷갈릴 수 있다. 아래의 그림을 참고하면 도움이 될 것이다.

무변성

제네릭 클래스를 인스턴스화 할 때 서로 다른 자료형을 인자로 사용하기 위해서는 자료형 사이의 상, 하위 관계를 잘 따져야 한다. 형식 매개변수 를 선언할 때 Out In 키워드 없이 그냥 선언하면 무변성으로 클래스가 선언된다. Nothing, Int, Any 자료형의 관계를 살펴보면 Nothing ↔ Int ↔ Any 의 상, 하위 관계를 가지고 있는데 이렇게 상하 관계가 명확해도 클래스가 무변성으로 선언되면 자료형 불일치 오류를 발생시킨다. 아래 코드를 살펴보자.

class Box<T>(val size : Int) // 무변성으로 제네릭 클래스 선언

fun main() {
	val anys : Box<Any> = Box<Int>(10) // 오류, 자료형 불일치
	val nothings : Box<Nothing> = Box<Int>(10) // 오류, 자료형 불일치

공변성

형식 매개변수 의 상, 하위 관계가 성립하고, 그 관계가 그대로 인스턴스 자료형으로 이어지는 경우 이를 공변성 이라고 한다.

class BoxOut T>(val size : Int) // 공변성으로 제네릭 클래스 선언

fun main() {
	val anys : Box<Any> = Box<Int>(10) // Int는 Any의 하위 클래스이므로 객체 생성 가능
	val nothings : Box<Nothing> = Box<Int>(10) // 오류, 자료형 불일치

Out 키워드를 사용해서 공변성으로 클래스를 선언했다. Any의 경우 Int의 상위 클래스이므로 Int형으로 생성한 객체도 Any에 포함할 수 있다. 하지만 Nothing의 경우 Int의 하위 클래스이므로 Int형으로 생성한 객체를 포함할 수 없다. (일반적인 상속처럼 생각하면 될 듯?)

공변성에서 자료형 제한하기

제네릭을 사용할 때 자료형 타입을 제한할 수 있다고 얘기했었다. 공변성에서도 똑같이 사용할 수 있다.

open class Animal(val size : Int) {
	fun feed() = println("Feeding")
}

class Cat(val jump : Int) : Animal(50)
class Dog(val weight : Int) : Animal(80)

class Box<out T : Animal>(val element : T) {
	// Out 키워드를 사용해서 공변성으로 선언
	// 따라서 하위 자료형이 상위 자료형으로 변환하는 것은 괜찮다.
}

fun main() {
	// 객체 생성
	val c1 : Cat = Cat(10)
	val c2 : Dog = Dog(20)
	
	var a1 : Animal = c1 // 하위 자료형이 상위 자료형으로의 변환은 가능.
	a1 = s1
	
	val c3 : Box<Cat> = Box<Animal>(10) // 상위 자료형이 하위 자료형으로의 변환은 불가능
	val c4 : Box<Any> = Box<Cat>(!0) // 자료형을 Animal로 제한했으므로 Animal, Cat, Dog를 제외한 다른 자료형은 사용불가.
}
profile
티스토리로 옮겼어요! (https://codekodo.tistory.com)
post-custom-banner

0개의 댓글