mm Home

Kotlin - Generics 본문

개발/Kotlin

Kotlin - Generics

jess_m 2019. 12. 4. 21:40

Generic

Kotlin의 Generic은 Java의 Jeneric과 비슷한 점이 많으므로 Java - Generics 글을 참고하면 좋을듯 하다.

위 글을 통해 아래의 내용을 이해하면 된다.



Type Erasure
컴파일러가 하위 호환을 위해 Type Parameter 정보를 지운다. (런타임시에 타입 파라미터 정보를 알 수 없다)


공변 (Covariant)
A -> A' 일때, C -> C<A'>
*공변은 같은 기저타입에서 같은 방향으로 변한다.


반공변 (Contravariant)
A -> A' 일때, C <- C<A'>
*불공변은 공변의 반대. 다른 방향으로 변한다.


in variable
out variable




대략적인 사용은 아래와 같다. Java와 비슷하다.

interface List<T> {
    fun test(i: Int): T
}

class A {
    // 타입 파라미터 상한
    fun <T : Number> List<T>.sum() : T
}
  • 타입 파라미터를 반드시 정의해야 한다
    • 타입 추론이 되는 경우 제외
  • 구체화(reified) 할 수 있다
  • 공변 (Covariant)
    • java : ? extends T
    • kotlin : in T
  • 반공변 (Contravariant)
    • java : ? super T
    • kotlin : out T
  • Wildcard 는 '*'

실체화 (Reification)

inline fun <reified T> readTo(t: T) {
    println(T::class)
    println(t)
}

//inline 키워드가 반드시 있어야 하는데, reified 키워드와 함께 있다면
//바이트코드를 복사하면서 구체화하기 떄문에 컴파일러가 타입을 알 수 있다. (빌드 시점에 타입 정보를 남기는 것)


readTo("sadasd")

//위의 코드를 사용하면 실제 컴파일이 되고 나면 아래와 같이 변경된다. 실제 inline 코드가 삽입될 때 Type까지 반영이 되는것.
println(String::class)
println("sadasd")

제약 사항

  • 타입 파라미터 클래스의 인스턴스 생성
  • 실체화하지 않은 타입을 파라미터로 받아 넘기기
  • inline 함수가 아닌 타입 파라미터를 reified로 지정할 수 없다

가변성

공변

  class C<in T> {
    fun doPrint(t: T) {
      println("do Something $t")
    }

    // 아래는 컴파일 에러. (공변 주었는데 write 하려고 하니까)
    fun test2(): T? {
      return null
    }
  }

  fun test() {
    //타입 파라미터가 Any 이므로 Any에 공변성을 가진다. 
    val c = C<Any>()
    c.doPrint(A())
    c.doPrint(B())
    c.doPrint("ABC")
    c.doPrint(123)
  }

반공변

open class A {}

class B : A() {}

// T의 bound type은 지정해주지 않아도 된다. 타입 추론에 의해 자동적으로 타입을 알 수 있기 때문이다. 
// 클래스 차원에서 사용가능한 타입파라미터를 제한할 수도 있다.
class D<out T>(val t: T) {
    fun getter(): T {
      return t;
    }

    // 아래는 컴파일 에러. (반공변 주었는데 read 하려고 하니까)
    fun setter(t: T) {
    }
  }

fun test() {
    // 타입 추론되어 타입 파라미터 생략
    val aGetter = D(A()).getter();  //A
    val bGetter = D(B()).getter();  //B
    val sGetter = D("string").getter(); //String
    val iGetter = D(123).getter();  //Int
}
  // 아래처럼 쓸 수도 있다.
  class D<out T : Int>(val t: T) {
    fun toDouble(): Double {
      return t.toDouble();
    }
  }

혹시 공변과 반공변을 같이 쓰고 싶다면 타입 파라미터를 하나 더 선언해야 한다.

class C<in T, out P>

'개발 > Kotlin' 카테고리의 다른 글

Kotlin Coroutines  (0) 2019.12.02
Comments