오늘도 더 나은 코드를 작성하였습니까?

사용 지점 변성: 타입이 언급되는 지점에서 변성지정 본문

Kotlin in Action/코틀린 답게 사용하기

사용 지점 변성: 타입이 언급되는 지점에서 변성지정

hik14 2021. 3. 3. 11:04

클래스를 선언하면서 변성을 지정하면 그 클래스를 사용하는 모든 곳에서 변성 지정자( in / out )가 영향을 주기 때문에 편리하다. 이런 방식을 선언 지점 변성이라 한다.

 

자바에서는 타입 파라미터가 있는 타입을 사용할 때마다 해당 타입 파라미터를 하위 타입이나 상위 타입 중 어떤 타입으로 대치할 수 있는지 명시해야 된다. 이런 방식을 사용 지점 변성이라 한다.

 

코틀린의 선언 지점 변성은 단 한번만 표시 하기 때문에 클래스를 사용하는 곳에서 변성에 대한 신경을 쓰지않아도 되기 때문에 코드가 간결하고 깔끔하다.

 

자바의 Stream 인터페이스

 

인터페이스를 사용하는 모든 곳에서 와일드카드를 만들어야 된다.

 

코틀린도 사용 지점 변성을 지원

 

 클래스 안에서 특정 타입 파라미터가 공변적 반공변적인지 알수 없어도 타입 파라미터가 나타나는 지점에 변성을 정할 수 있다.

 

MutableList 같은 상당수의 인터페이스는 타입 파라미터로 지정된 타입을 소비하는 동시에 생산할 수 있기 때문에 공변, 반공변적이지도 않다. 하지만 인터페이스 타입의 변수가 한 함수 안에서 생산자 소비자 중 단 한 가지만 역활을 담당할 때가 있다.

 

fun <T> copyData(source: MutableList<T>,
                 destination: MutableList<T>){

    for(item in source){
        destination.add(item)
    }
}

이 함수는 컬렉션 원소를 다른 컬렉션으로 복사한다. 두개의 변경 가능한 리스트라 무공변타입이지만, 원본에서는 읽어내는 작업만 실행하고, 대상에서는 쓰기 작업만 실시한다.

 

이 함수가 여러 다른 리스트 타입에 대해 작동하도록 만들어보자.

fun <T: R ,R> copyData(source: MutableList<T>,
                 destination: MutableList<R>){
    for(item in source){
        destination.add(item)
    }
}

source의 타입은 destination타입의 하위 타입으로 제한한다. 이것을 변성변경자를 통해 만들자.

 

변성 변경자는 타입 파라미터 사용하는 어느 위치(파리미터 타입, 로컬변수 타입, 함수반환 타입)든 들어갈 수 있다.

이때 타입 프로젝션이 일어난다.

 

즉,  source를 일반적인 MutableList가 아니라 copyData함수의 MutableList에 제약을 가한 타입이다.

메소드에서 반환타입으로 파라미터 T 를 사용하는 메소드만 호출 가능하다.

 

out 프로젝션 타입을 파라미터로 사용한 데이터 복사.

fun <T> copyData(source: MutableList<out T>,
                 destination: MutableList<T>){
    for(item in source){
        destination.add(item)
    }
}

컴파일러 오류 - Kotlin: The integer literal does not conform to the expected type Nothing

fun main() {
    val list: MutableList<out Number> = mutableListOf(1,2,3,4)
    list.add(42)
}

copyData를 제대로 구현하려면 List<T>를 source인자의 타입으로 정하는 것이다.

실제 MutableList가 아니라 List에 있는 메소드만 source에 대해 사용하면되고 

List의 타입 파라미터 공변성은 List선언에 있다.

 

 

in 프로젝션 타입을 파라미터로 사용한 데이터 복사.

원본리스트의 상위타입을 대상리스트의 상위타입으로 허용하여 한다. 

fun <T> copyData(source: MutableList<T>, destination: MutableList<in T>){
    for (item in source){
        destination.add(item)
    }
}