Zoom In, Zoom Out
코틀린에서 in과 out 키워드는 제네릭 타입 파라미터의 공변성(variance)을 제어하기 위해 사용된다.
이는 제네릭 클래스나 함수가 상위 타입과 하위 타입 간의 관계를 명확히 하여 타입 안전성을 보장하도록 도와준다.
우선 코틀린의 in, out을 각각 살펴보면
out 키워드는 주로 생산자(Producer) 역할의 제네릭 타입에 사용되며, 제네릭 타입이 반환값으로만 사용될 때 적합하다. out 키워드를 사용하면 제네릭 타입이 자신의 하위 타입으로 대체 가능해진다.
open class Language
class Kotlin: Language()
class Swift: Language()
class Developer<out T>(private val language: T) {
fun getLanguage(): T = language
}
@Test
fun developerTest() {
val kotlinDeveloper: Developer<Kotlin> = Developer(Kotlin())
val developer: Developer<Language> = kotlinDeveloper
// out이 없다면 Type mismatch 발생
println(developer.getLanguage())
}
위 코드에서 Developer<out T>
out 키워드가 없다면 Type mismatch가 발생한다.
out: 꺼내와서(out 시킨다) 읽는다. -> Write은 불가능
부모 클래스에 자식 클래스를 사용가능하게 해준다.
in 키워드는 소비자(Consumer) 역할의 제네릭 타입에 사용되며, 제네릭 타입이 입력 파라미터로만 사용될 때 적합하다. in 키워드를 사용하면 제네릭 타입이 자신의 상위 타입으로 대체 가능해진다.
open class Language
class Kotlin: Language()
class Swift: Language()
class Developer<in T: Language> {
fun printLanguage(language: T) {
println("this language is $language")
}
}
@Test
fun developerTest() {
val developer: Developer<Language> = Developer()
val kotlinDeveloper: Developer<Kotlin> = developer
// in이 없다면 Type mismatch 발생
kotlinDeveloper.printLanguage(Kotlin())
}
서버 클래스에 슈퍼 클래스를 사용가능하게 해준다.
제네릭 타입 파라미터에 in 또는 out 키워드를 사용하지 않으면, 해당 제네릭 타입은 무공변성(invariance)을 가지며, 명시된 타입만 허용된다. 즉, 하위 타입도 상위 타입으로 대체할 수 없고, 반대도 불가능하다.
open class Language
class Kotlin: Language()
class Swift: Language()
class Developer<T>(private var value: T) {
fun get(): T = value
fun set(newValue: T) { value = newValue }
}
val kotlinDeveloper: Developer<Kotlin> = Developer(Kotlin())
val developer: Developer<Language> = kotlinDeveloper // Type mismatch. 발생
코틀린의 in과 out 키워드는 자바의 와일드카드(? extends T와 ? super T)와 개념적으로 유사하지만, 언어 차원에서 조금 다르게 작동한다. 코틀린의 in과 out은 제네릭 타입 선언에서 공변성(variance)을 지정하기 위한 키워드인 반면, 자바의 와일드카드는 타입 사용 시 변수를 선언하는 데 사용된다.
- 코틀린 out: out은 코틀린의 제네릭 타입에서 공변성(Covariance)을 지정하는 키워드. 이는 해당 타입이 반환만 가능하고, 소비(입력)될 수 없다는 것을 의미한다.
- 자바 ? extends T: 자바의 ? extends T 와일드카드는 제네릭 타입이 T의 하위 타입만 허용한다는 의미. 주로 읽기 전용으로 사용되며, 안전하게 T의 하위 타입을 반환할 수 있게 한다.
- 코틀린 in: in은 제네릭 타입이 반공변성(Contravariance)을 가지도록 만든다. 즉, 해당 타입은 소비자(Consumer) 역할을 하며, T의 상위 타입을 허용한다. 반공변성으로 지정된 타입은 입력용 파라미터로만 사용할 수 있다.
- 자바 ? super T: 자바의 ? super T 와일드카드는 제네릭 타입이 T의 상위 타입만 허용하게 만든다. T 타입의 값을 안전하게 소비(입력)할 수 있다.
주요 차이점을 정리 하자면