15. Generics

제네릭

치트시트

  • class Box<T> → 제네릭 클래스

  • fun <T> foo(t: T) → 제네릭 함수

  • 가변성:

    • out T = ? extends T (읽기 전용)

    • in T = ? super T (쓰기 전용)

  • 제한: <T : Number>

  • inline + reified → 런타임 타입 사용 가능 (JSON 등)

  • 서버 활용: Repository, API Wrapper, 직렬화/역직렬화


1) 기본 제네릭 선언

class Box<T>(val value: T)

val intBox = Box(1)
val strBox = Box("Hello")
  • T는 타입 파라미터

  • 타입 추론이 가능하면 명시하지 않아도 됨


2) 함수의 제네릭

fun <T> singletonList(item: T): List<T> = listOf(item)

val l1 = singletonList(42)       // List<Int>
val l2 = singletonList("kotlin") // List<String>
  • fun <T>로 제네릭 함수 선언 가능


3) 가변성 (Variance) — out / in

자바의 <? extends T>, <? super T> 개념을 코틀린은 out, in 키워드로 단순화.

out (공변, covariant)

  • “읽기 전용” → 반환 타입으로만 사용 가능

interface Producer<out T> {
    fun produce(): T
}

in (반공변, contravariant)

  • “쓰기 전용” → 파라미터 타입으로만 사용 가능

interface Consumer<in T> {
    fun consume(item: T)
}

👉 자바에서는 와일드카드(? extends, ? super)로 해결했지만, 👉 코틀린은 타입 선언 시점에 variance를 고정하여 가독성과 안전성을 높임.


4) 제네릭 제한 (Upper bounds)

fun <T : Number> sum(a: T, b: T): Double =
    a.toDouble() + b.toDouble()
  • T : Number → Number 또는 하위 타입만 허용

  • 다중 제한도 가능: <T> where T: CharSequence, T: Comparable<T>


5) reified 타입 파라미터 (실행 시 타입 유지)

  • JVM 제네릭은 타입 소거(Type Erasure) 때문에 런타임에 타입 정보가 사라짐

  • 코틀린은 inline 함수 + reified 조합으로 런타임에 타입 사용 가능

inline fun <reified T> Gson.fromJson(json: String): T =
    fromJson(json, T::class.java)

val user: User = gson.fromJson<User>(json)

👉 서버 개발에서 JSON 직렬화/역직렬화 시 매우 자주 활용.


6) 서버 개발 활용 패턴

A. 공통 Repository

interface Repository<T> {
    fun findById(id: Long): T?
}

B. API 응답 Wrapper

data class ApiResponse<T>(
    val status: String,
    val data: T?
)

C. JSON 직렬화

inline fun <reified T> parse(json: String): T =
    jacksonObjectMapper().readValue(json, T::class.java)

Last updated