fun main() {
val generics = MyGenerics<String>("테스트")
}
또한 인자를 통해 타입 추론이 가능한 경우 타입 아규먼트를 생략할 수 있다.
val generics = MyGenerics("테스트")
타입 추론이 가능하기 때문에 변수의 타입에 타입 아규먼트를 추가해도 되고 그렇지 않은 경우 타입 아규먼트를 생성자에서 추가해도 된다.
val list1: MutableList<String> = mutableListOf()
val list2 = mutableListOf<String>()
어떤 타입이 들어올지 알 수 없지만 안전하게 사용하고 싶은 경우 스타 프로젝션 구문을 제공한다.
val list: List<*> = listOf<String>("테스트")
변성
제네릭에서 파라미터화된 타입이 서로 어떤 관계에 있는지 설명하는 개념
변성은 크게 공변성, 반공변성 그리고 무공변성으로 나뉜다.
이펙티브 자바에선 공변성과 반공변성을 설명할 때 PECS 규칙을 언급한다.
Producer-Extends, Consumer-Super
공변성은 자바 제네릭의 extends 키워드이고 코틀린에선 out
반공변성은 자바 제네릭의 super 키워드이고 코틀린에선 in
공변성
공변성을 쓰지 않아 문제가 되는 케이스
아래의 코드에서 실수로 String이 아닌 타입을 넣을 경우를 막기 때문에 무의미한 컴파일 오류가 아니지만 특정 상황에서는 공변성이 필요할 수 있다.
그러한 경우 공변성 키워드인 out을 사용해 해결한다.
변경 전
class MyGenerics<T>(val t: T)
fun main() {
val generics = MyGenerics("테스트")
val charGenerics : MyGenerics<CharSequence> = generics // 컴파일 오류
}
<br>
- 변경 후
* 공변성은 CharSequence가 String의 상위 타입일때 `MyGenerics<CharSequence>`가 `MyGenerics<String>`의 상위 타입이므로 공변성이다.
``` kotlin
class MyGenerics<out T>(val t: T)
fun main() {
val generics = MyGenerics<String>("테스트")
val charGenerics : MyGenerics<CharSequence> = generics
}
반공변성
반공변성을 쓰지 않아 문제가 되는 케이스
아래 코드에 반공변성 키워드인 in을 사용해 상위 타입도 전달 받을 수 있도록 한다.
변경 전
class Bag<T> {
fun saveAll(
to: MutableList<T>,
from: MutableList<T>,
) {
to.addAll(from)
}
}
<br><br>
- 변경 후
* 공변성과는 반대로 String이 CharSequence의 하위 타입일 때 Bag<String>가 Bag<CharSequence>의 상위 타입이 되므로 반공변성이다.
* 기본은 무공변성으로써 in,out 어떤 것도 지정하지 않은 경우
* String은 CharSequence의 하위 타입이지만 MyGenerics<String>와 MyGenerics<CharSequence>는 아무런 관계도 아니다.
* 이것이 무공변성이다.
``` kotlin
class Bag<T> {
fun saveAll(
to: MutableList<in T>,
from : MutableList<in T>,
) {
to.addAll(from)
}
}
fun main() {
val bag = Bag<String>()
bag.saveAll(mutableListOf<CharSequence>(""), mutableListOf<String>(""))
}
제네릭
코틀린의 제네릭
타입 파라미터
를 가질 수 있다.타입 아규먼트
를 제공하면 된다.스타 프로젝션 구문
을 제공한다.변성
PECS
규칙을 언급한다.extends
키워드이고 코틀린에선out
super
키워드이고 코틀린에선in
공변성
out
을 사용해 해결한다.fun main() { val generics = MyGenerics("테스트")
}
반공변성
in
을 사용해 상위 타입도 전달 받을 수 있도록 한다.변경 전
fun main() { val bag = Bag()
}