1 minute read

코틀린도 자바와 같이 Genric을 지원한다. 일반적으로 자바와 다르지 않으나, 코틀린은 좀더 많은 기능을 지원한다. 자바는 제네릭 개념이 들어가면ㅁ서 하위 호환성을 위해 타입을 정의하지 않고도 사용할 수 있으나, 코틀린은 반드시 타입을 정의하고 써야한다.

Extension Property

fun main() {
  val letters = ('a'..'z').toList()
  println(letters.slice<Char>(0..2))
  println(letters.slice(10..13))
}

extension property만 제네릭 하게 만들 수 있다. 일반 property에 제네릭을 사용하면 컴파일 오류가 난다.

제네릭 클래스

# JAVA
public class Car<T> {
  private T data;
  
  public void set(T data) {
    this.data = data
  }
  
  public T get() {
    return data;
  }
}

#Kotlin
class Car<T> {
  private var data:T? = null
    get() = { return data }
    set(value) = { field = value }

자바와 같은 방법으로 <>형태로 선언하면 된다.

Type parameter limitation

자바에는 extends나 super를 사용하여 사용한 타입을 제한할 수 있다. 코틀린에서는 :를 사용하여 상한 typ (upper bound)를 설정 할 수 있다.

# JAVA
<T extends Number> T sum(List<T> list)

# Kotilin
fun <T: Number> List<T>.sum(): T

코틀린에서 타입 상한을 설정하면 상한 타입으로 취급할 수 있다.

fun <T: Comparable<T>> max(first: T, second: T): T {
  return if (first > second) first else second
}

fun main() {
  println(max("kotlin", "java"))
}

위 예제에서는 T는 Comparable 상한을 갖는다. 즉 max()를 사용할때의 인자는 Comparable을 구현하고 있어야 한다는 의미가 된다.


fun <T> ensureTrailingPeriod(seq: T)
  where T: CharSequence, T: Appendable {
  
  if (!seq.endWith('.')) {
    seq.append('.')
  }
}

fun main() {
  val helloWorld = StirngBuilder("Hello World")
  ensureTrailingPeriod(helloWorld)
  println(helloWorld)
}

두개의 제약을 하는 경우 where 연산자를 씁니다. 이때의 함수 타입은 나열된 타입들은 전부 만족해야 한다.

Non-Null Type parameter

코틀린에서 타입의 기본은 Non null이다. 다만 제네릭인 경우에만 기본값이 nullable이다.

class Friend<T> {
  fun getUniqueIt(value: T) {
    value?.hashCode()
  }
}

fun main() {
  val friend = Friend()
  friend.getUniqueId(null)
}

따라서 NonNull인 타입으로 제한하려면 명시적으로 <T: Any>로 선언해야 한다.

만 선언한다면 <T: Any?>와 같다. ## Generic의 runtime 동작 자바에서는 실행시점에 instance의 타입인자를 확인할 수 없다. 이는 JVM이 type erase를 하기 때문이다. 다만 코틀린에서는 inline을 통해서 이를 피할 수 있다. 클래스가 생성되어 instance가 되면 더 이상 인자 정보를 가지고 있지 않는다. 예를 들어 List을 객체로 만들었다면, 이는 List 타입만 알 뿐 내부에 저장된 원소type의 정보는 알 수 없다.

Categories:

Updated: