0Pricing
Kotlin Academy · 강의

where를 사용한 제네릭 함수와 타입 제약

하나 또는 여러 타입 제약이 있는 제네릭 함수를 작성해 보세요.

where를 사용한 제네릭 함수와 타입 제약은(는) CoddyKit의 무료 Kotlin Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Kotlin Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

제네릭 함수 기초

제네릭 함수는 함수 이름 앞의 꺾쇠괄호 안에 타입 매개변수를 작성합니다. 타입 매개변수는 매개변수 타입과 반환 타입에 사용할 수 있습니다.

fun <T> identity(value: T): T = value
fun <T> listOf2(a: T, b: T): List<T> = listOf(a, b)
fun main() {
    println(identity(42))       // 42
    println(identity("hello"))  // hello
    println(listOf2(1, 2))      // [1, 2]
}

콜론(:)을 사용한 상한 제약

:을 사용해 상한을 지정하여 타입 매개변수를 제한합니다. 타입은 상한의 하위 타입이어야 합니다.

fun <T : Comparable<T>> max(a: T, b: T): T = if (a >= b) a else b
fun main() {
    println(max(3, 7))          // 7
    println(max("apple", "banana")) // banana
    // max(listOf(1), listOf(2)) // error: List is not Comparable
}

널 허용 상한

기본 상한은 Any?이며 널을 허용합니다. 널을 제외하려면 T : Any를 사용합니다.

fun <T : Any> notNull(value: T?): T {
    return value ?: throw IllegalArgumentException("Value is null")
}
fun main() {
    println(notNull("hello"))  // hello
    try { notNull(null) } catch (e: Exception) { println(e.message) }
}

where를 사용한 여러 제약 조건

where 절을 사용해 하나의 타입 매개변수에 여러 제약 조건을 적용합니다.

fun <T> process(item: T): String
        where T : Comparable<T>, T : CharSequence {
    return "Length=${item.length}, sorted=${item > "a"}"
}
fun main() {
    println(process("kotlin")) // Length=6, sorted=true
    // process(42) // error: Int doesn't implement CharSequence
}

클래스에서의 where

where 절은 클래스 수준의 타입 매개변수에도 사용할 수 있습니다.

class SortedContainer<T>(private val items: MutableList<T> = mutableListOf())
        where T : Comparable<T>, T : Any {
    fun add(item: T) { items.add(item); items.sort() }
    fun top(): T? = items.lastOrNull()
}
fun main() {
    val c = SortedContainer<Int>()
    c.add(5); c.add(2); c.add(8)
    println(c.top()) // 8
}

제네릭 확장 함수

확장 함수에도 타입 매개변수를 사용할 수 있으므로, 특정 제네릭 타입에서 해당 함수를 사용할 수 있습니다.

fun <T : Comparable<T>> List<T>.second(): T? {
    return if (size >= 2) this[1] else null
}
fun main() {
    println(listOf(10, 20, 30).second()) // 20
    println(listOf("a").second())        // null
}

반환 위치의 타입 매개변수

제네릭 함수는 문맥에서 타입을 추론할 수 있으므로 호출 위치에 타입 인수를 명시하지 않아도 됩니다.

fun <T> MutableList<T>.popOrDefault(default: T): T {
    return if (isEmpty()) default else removeAt(lastIndex)
}
fun main() {
    val list = mutableListOf(1, 2, 3)
    println(list.popOrDefault(0)) // 3
    println(list.popOrDefault(0)) // 2
    println(mutableListOf<Int>().popOrDefault(-1)) // -1
}

재사용 가능한 제네릭 유틸리티

제네릭 함수를 사용하면 중복 없이 여러 타입에서 유틸리티 함수를 재사용할 수 있습니다.

fun <T> Iterable<T>.firstOrElse(default: T): T = firstOrNull() ?: default
fun <T, R : Comparable<R>> Iterable<T>.maxByOrElse(selector: (T) -> R, default: T): T =
    maxByOrNull(selector) ?: default
fun main() {
    println(listOf<String>().firstOrElse("fallback")) // fallback
    println(listOf("a","bbb","cc").maxByOrElse({ it.length }, "")) // bbb
}

스타 프로젝션과 제한된 와일드카드

T를 생성하거나 소비해야 할 때 유용한 제한된 타입 매개변수와, 타입을 알 수 없을 때 사용하는 스타 프로젝션을 비교해 보겠습니다.

fun <T : Number> sumList(list: List<T>): Double =
    list.sumOf { it.toDouble() }
// Star projection: read-only, type unknown
fun printAll(list: List<*>) = list.forEach { println(it) }
fun main() {
    println(sumList(listOf(1, 2, 3)))    // 6.0
    printAll(listOf("a", 1, true))       // a, 1, true
}

제네릭 쌍 교환

쌍의 구성 요소 순서를 뒤집는 간단하지만 설명에 유용한 제네릭 함수입니다.

fun <A, B> Pair<A, B>.swap(): Pair<B, A> = Pair(second, first)
fun main() {
    val pair = Pair("hello", 42)
    val swapped = pair.swap()
    println(swapped) // (42, hello)
}

실습: 제네릭 결과 래퍼 해제

제한된 제네릭을 사용해 타입이 지정된 변환으로 Result<T>를 변환하는 유틸리티를 작성합니다.

fun <T : Any, R : Any> Result<T>.mapNotNull(transform: (T) -> R?): Result<R> {
    return fold(
        onSuccess = { v ->
            val r = transform(v)
            if (r != null) Result.success(r) else Result.failure(NoSuchElementException())
        },
        onFailure = { Result.failure(it) }
    )
}
fun main() {
    val r = Result.success("123")
    val n = r.mapNotNull { it.toIntOrNull() }
    println(n) // Success(123)
}

빠른 확인

하나의 타입 매개변수에 여러 제약 조건을 적용하려면 어떻게 해야 합니까?

복습

제네릭 함수는 타입 매개변수를 사용해 재사용 가능하고 타입 안전한 코드를 작성하게 해 줍니다. 하나의 상한에는 :을 사용하고, 여러 상한에는 where를 사용해 제한합니다. Kotlin 컴파일러는 호출 위치에서 제약 조건을 검사합니다.

자주 묻는 질문

“where를 사용한 제네릭 함수와 타입 제약” 강의는 무료인가요?

네 — “where를 사용한 제네릭 함수와 타입 제약” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Kotlin Academy 강의 전체를 잠금 해제할 수 있습니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“where를 사용한 제네릭 함수와 타입 제약”에서 뭘 배우나요?

하나 또는 여러 타입 제약이 있는 제네릭 함수를 작성해 보세요. 브라우저에서 직접 실행하는 실습 코드로 Kotlin Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Kotlin Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Kotlin Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“where를 사용한 제네릭 함수와 타입 제약” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Kotlin Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Kotlin Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. where를 사용한 제네릭 함수와 타입 제약
  2. 선언 위치 변성: in과 out
  3. 스타 프로젝션과 *를 사용할 때
  4. 타입 소거와 reified 타입 매개변수
← Kotlin Academy(으)로 돌아가기