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