PHpullh
학습 라이브러리/Kotlin/고급 제네릭 — in/out variance

KOTLIN · 객체지향

고급 제네릭 — in/out variance

Kotlin의 out(공변)과 in(반공변) 키워드는 제네릭 타입의 변성(variance)을 선언합니다. Java의 ? extends T? super T를 대체하며, 타입 안전한 제네릭 API 설계에 필수적입니다.

객체지향고급genericsvariancecovariancecontravariancetype-safety

핵심 설명

Kotlin의 out(공변)과 in(반공변) 키워드는 제네릭 타입의 변성(variance)을 선언합니다. Java의 ? extends T? super T를 대체하며, 타입 안전한 제네릭 API 설계에 필수적입니다.

Kotlin code

// out = 공변 (Producer): T를 반환만 함
interface Producer<out T> {
    fun produce(): T
    // fun consume(item: T) // 컴파일 오류! out 타입은 입력 불가
}

// in = 반공변 (Consumer): T를 소비만 함
interface Consumer<in T> {
    fun consume(item: T)
    // fun produce(): T // 컴파일 오류! in 타입은 반환 불가
}

// 무공변 (Invariant): T를 입출력 모두 사용
interface Transformer<T> {
    fun transform(item: T): T
}

open class Animal(val name: String)
class Dog(name: String) : Animal(name)
class Cat(name: String) : Animal(name)

class DogProducer : Producer<Dog> {
    override fun produce() = Dog("멍멍이")
}

class AnimalConsumer : Consumer<Animal> {
    override fun consume(item: Animal) {
        println("동물 처리: ${item.name}")
    }
}

fun feedAnimals(producer: Producer<Animal>, consumer: Consumer<Dog>) {
    val animal = producer.produce()
    consumer.consume(Dog("강아지"))
}

fun main() {
    // out: Dog는 Animal의 하위 → Producer<Dog>는 Producer<Animal>의 하위
    val dogProducer: Producer<Animal> = DogProducer() // OK!
    println(dogProducer.produce().name)

    // in: Animal은 Dog의 상위 → Consumer<Animal>은 Consumer<Dog>의 하위
    val animalConsumer: Consumer<Dog> = AnimalConsumer() // OK!
    animalConsumer.consume(Dog("바둑이"))

    feedAnimals(DogProducer(), AnimalConsumer())
}

학습 팁

기억법: out은 "나가기만"(produce), in은 "들어오기만"(consume). List<out T>는 읽기 전용이라 List<Dog>List<Animal>로 안전하게 사용할 수 있습니다.

주의할 점

타입 파라미터에 out을 선언하면 해당 타입을 함수 매개변수(입력 위치)에 사용할 수 없습니다. 입출력 모두 필요하면 변성을 선언하지 말고 사용처 변성(use-site variance)을 활용하세요.

자주 묻는 질문

고급 제네릭 — in/out variance란 무엇인가요?

Kotlin의 out (공변)과 in (반공변) 키워드는 제네릭 타입의 변성(variance)을 선언합니다. Java의 ? extends T 와 ? super T 를 대체하며, 타입 안전한 제네릭 API 설계에 필수적입니다.

고급 제네릭 — in/out variance 학습 시 주의할 점은 무엇인가요?

타입 파라미터에 out 을 선언하면 해당 타입을 함수 매개변수(입력 위치)에 사용할 수 없습니다. 입출력 모두 필요하면 변성을 선언하지 말고 사용처 변성(use-site variance)을 활용하세요.