[코틀린] Generics

kotlin의 Generics을 정리 Java에서 사용하는 Generics과 동일하게 사용할 수 있지만, Kotlin은 Generics 정의한 클래스를 상속받을 때 명시적으로 지정해야 한다.

다시 말해 java에서는 제네릭 정의를 하지 않아도 기본 Object로 만들어주지만, 코틀린에서는 명시적으로 꼭 적어주도록 만들었다.

Java와 Kotlin의 Generics 차이

자바에서의 Generics는

//Java
interface Generic<T> {
  void setItem(T item);
}

이렇게 사용하는데 반해 Kotlin은

//Kotlin
interface Generic<in T> {
  fun setItem(item: T)
}

이렇게 정의하게 된다. 반드시 Generics를 지정해줘야한다. 상속받아 구현하려면 다음과 같이 하면 된다.

class Sample : Generic<Generic Type 정의> {

    override fun setItem(item: Generic Type 정의) {
        // 구현
    }
}

Wildcard tpye argument

Java에는 와일드카드라 불리는 부분이 있습니다. Kotlin에서는 in/out을 명시해야 하는데 이러한 부분을 이해하기 위해서 Java의 와일드카드를 먼저 살펴보자

이러한 형태는 보통 List 내부 구현체에서 많이 볼 수 있는데 다음과 같이 addAll의 Collection에 extends로 정의한 것을 볼 수 있다.

코틀린은 Generics을 정의할 때 in/out을 자동으로 표현할 수 있도록 워닝 메시지를 출력한다. option + enter을 함께 눌러서 in/out을 자동으로 표현할 수 있으니 참고

Wildcard type argument - java

먼저 Java의 와일드카드를 살펴보면, 크게 extends와 super로 구분되어 있다. 각각의 의미는 아래와 같다.

T : read/write 모두 가능

? extends T : read만 가능한 서브타입 와일드카드

? super T : write만 가능한 슈퍼 타입 와일드카드

Wildcard type argument - kotlin

kotlin에서는 명시적으로 super/extends 대신 in/out 키워드를 제공하는데, 각각의 정의와 매칭은 아래와 같다.

T : 별도의 Wildcard 정의가 없이 read/write 모두 가능

in T : Java의 ? super T와 같음. input의 약자이며 write 만 가능

out T : Java의 ? extends T와 같음. output의 약자이며 read 만 가능

kotlin에서도 java처럼 테스트하기 위해 generics interface을 아래와 같이 정의하였다.

interface Output<T> {
    fun isArgument(argument: T): Boolean
}

사용하는 ArrayList의 아이템을 다음과 같이 2개 정의할 수 있다.


class ExampleUnitTest {

    val items = ArrayList<Output<String>>()

    init {
        items.add(object : Output<String> {
            override fun isArgument(argument: String) = false
        })
        items.add(object : Output<String> {
            override fun isArgument(argument: String) = true
        })
    }
}