Kotlin Generic 정리

패스트 캠퍼스 실무 프로젝트로 배우는 Kotlin & Spring 강의 정리

Generic

  • 제네릭을 선언할 때 타입 파라미터(<T>)를 사용하여 선언
  • 클래스를 생성할 때 생성자의 파라미터에 의해 타입을 추론할 수 있으므로 타입 인자 생략 가능
class Sample<T>(val t: T)

fun main() {
  val sample = Sample<String>("sample1")
  
  val sample2 = Sample("sample2")
}
  • 변수의 타입에 제네릭을 사용하거나 생성자에서 타입 인자를 추가할 수 있음
fun main() {
  val list1: MutableList<String> = mutableListOf()
  
  val list2 = mutableListOf<String>()
}
  • Star projections(<*>)
    • 어떤 타입 인자를 받을 지 알 수 없지만, 안전하게 사용하고 싶을 때 사용
fun main() {
  val list: List<*> = listOf("a", "b", "c")
  
  val list2: List<*> = listOf(1, 2, 3)
}
  • 변성

    • 타입 파라미터화 된 타입의 관계를 설명하는 개념
    • PECS: Effective Java에서 Producer는 extends, Consumer는 Super를 사용하는 규칙
    • 무공변(invariant): 타입 인자들 사이에 하위 타입 관계가 있어도 아무 관계가 없는 타입인 것으로 간주
      • 제네릭을 쓸 때 기본적으로 무공변성
    • 공변(covariant): 타입 인자의 상하위 타입 관계에 따라 제네릭 타입의 상하윕 타입 관계가 함께 변하는 것
      • 자바 제네릭의 extends에 해당, 코틀린에서는 out
    class MyGenerics<T>(val t: T)
      
    fun main() {
      val generics = MyGenerics<String>("test")
        
      // String은 CharSequence의 서브 타입
      // charGenerics에 generics를 할당할 수 있을 것 같지만, 컴파일 에러 발생
      val charGenerics : MyGenerics<CharSequence> = generics
    }
    
    // out 키워드를 사용하여 공변성 부여
    class MyGenerics<out T>(val t: T)
      
    fun main() {
      val generics = MyGenerics<String>("test")
        
      // 컴파일 에러 발생하지 않음
      val charGenerics : MyGenerics<CharSequence> = generics
    }
    
    • 반변(contravariant)
      • 자바 제네릭의 super에 해당, 코틀린에서는 in
    class Bag<T> {
        fun saveAll(to: MutableList<T>, from: MutableList<T>) {
            to.addAll(from)
        }
    }
      
    fun main() {
        val bag = Bag<String>()
      		
      	// 컴파일 에러 발생
        bag.saveAll(mutableListOf<CharSequence>("1", "2"), mutableListOf<String>("3", "4"))
    }
    
    // in 키워드 사용하여 반공변성 사용
    class Bag<T> {
        
      	// MutableList<T> 타입이 MutableList<in T> 보다 상위 타입이 됨
        fun saveAll(to: MutableList<in T>, from: MutableList<T>) {
            to.addAll(from)      
        }
    }
      
    fun main() {
        val bag = Bag<String>()
        bag.saveAll(mutableListOf<CharSequence>("1", "2"), mutableListOf<String>("3", "4"))
    }