0Pricing
Kotlin Academy · 강의

선언 위치 변성

in과 out

선언 위치 변성은(는) CoddyKit의 무료 Kotlin Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Kotlin Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

변성 문제

List<String>은 List<Any>의 한 종류일까요? 어떤 경우에는 그렇고, 어떤 경우에는 그렇지 않습니다. 변성은 제네릭 타입의 타입 매개변수 사이의 관계에 따라 제네릭 타입들이 어떻게 관계되는지를 나타냅니다. 코틀린에서는 이를 out과 in으로 표현합니다.

기본적으로 불변

기본적으로 제네릭은 불변입니다. String이 모든 타입의 하위 타입이더라도 Box<String>은 Box<Any>가 아닙니다. 이렇게 하면 안전하지 않은 연산을 방지할 수 있습니다.

class Box<T>(val value: T)

fun main() {
    val strBox = Box("hi")
    // val anyBox: Box<Any> = strBox // would not compile
    println(strBox.value)
}

출력 지정자를 사용한 공변성

타입 매개변수에 out을 표시하면 공변이 됩니다. 즉, Producer<String>은 Producer<Any>의 하위 타입이 됩니다. 이 타입은 출력 위치(반환 타입)에만 나타날 수 있습니다.

class Producer<out T>(private val item: T) {
    fun produce(): T = item
}

fun main() {
    val strProducer: Producer<String> = Producer("hello")
    val anyProducer: Producer<Any> = strProducer
    println(anyProducer.produce())
}

출력 지정자가 안전한 이유

클래스가 T를 생성해 반환하기만 하고 소비하지 않는다면, Producer<String>을 Producer<Any>로 취급해도 안전합니다. 반환하는 모든 String은 모든 타입에 해당하기 때문입니다.

표준 라이브러리의 출력 변성

코틀린의 읽기 전용 List<out E>은 공변이므로 List<String>을 List<Any>가 필요한 곳에서 사용할 수 있습니다.

fun printAll(items: List<Any>) {
    items.forEach { println(it) }
}

fun main() {
    val names: List<String> = listOf("Ann", "Bob")
    printAll(names)
}

입력 지정자를 사용한 반공변성

타입 매개변수에 in을 표시하면 반공변이 됩니다. 즉, Consumer<Any>는 Consumer<String>의 하위 타입이 됩니다. 이 타입은 입력 위치(매개변수)에만 나타날 수 있습니다.

class Consumer<in T> {
    fun consume(item: T) {
        println("Consumed $item")
    }
}

fun main() {
    val anyConsumer: Consumer<Any> = Consumer()
    val strConsumer: Consumer<String> = anyConsumer
    strConsumer.consume("hi")
}

입력 지정자가 안전한 이유

모든 타입을 받아들이는 소비자는 당연히 String도 받아들일 수 있습니다. 따라서 Consumer<Any>는 Consumer<String>이 필요한 곳에서 대신 사용할 수 있습니다.

생산자와 소비자 기억법

Producer는 하위 타입을 확장하고 Consumer는 상위 타입을 사용한다고 기억하거나, 코틀린 방식으로는 생산자(출력)에 out을, 소비자(입력)에 in을 사용한다고 기억하십시오. 타입이 T를 출력하기만 하면 out을 사용하고, T를 입력받기만 하면 in을 사용합니다.

입력과 출력의 결합

입력과 출력 모두에 사용되는 타입 매개변수(변경 가능한 목록에서처럼)는 불변으로 유지해야 합니다. Function 타입은 두 경우를 모두 보여 줍니다. 매개변수에는 in이 사용되고 반환 값에는 out이 사용됩니다.

fun main() {
    val f: (Number) -> Int = { it.toInt() }
    val g: (Int) -> Number = f
    println(g(3.0.let { Number::class; 5 }))
}

선언 위치는 한 곳에서 지정

선언 위치 변성은 클래스 자체에서 in/out과 함께 한 번만 선언합니다. 그러면 클래스를 사용할 때마다 변성이 자동으로 적용됩니다. 사용할 때마다 와일드카드를 반복하는 Java와는 다릅니다.

입력 또는 출력 지정자 선택

다음과 같이 판단하십시오. 이 클래스가 T를 생성하기만 한다면 out을 사용하고, T를 소비하기만 한다면 in을 사용하며, 둘 다 한다면 불변으로 둡니다. 역할에 맞는 지정자를 사용하면 제네릭 타입을 유연하고 안전하게 만들 수 있습니다.

빠른 확인

out 지정자는 무엇을 나타냅니까?

복습

제네릭은 기본적으로 불변입니다. 타입이 값만 생성할 때는 공변성을 위해 out을 사용하고, 값만 소비할 때는 반공변성을 위해 in을 사용하십시오. 생산자에는 출력 지정자를, 소비자에는 입력 지정자를 사용한다는 점을 기억하십시오. 다음에는 타입 프로젝션을 사용해 사용하는 지점에서 변성을 적용합니다.

자주 묻는 질문

“선언 위치 변성” 강의는 무료인가요?

네 — “선언 위치 변성” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Kotlin Academy 강의 전체를 잠금 해제할 수 있습니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“선언 위치 변성”에서 뭘 배우나요?

in과 out 브라우저에서 직접 실행하는 실습 코드로 Kotlin Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Kotlin Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Kotlin Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“선언 위치 변성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Kotlin Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Kotlin Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 제네릭 함수와 클래스
  2. 선언 위치 변성
  3. 사용 위치 변성
  4. 제네릭 제약
← Kotlin Academy(으)로 돌아가기