본문 바로가기

Kotlin

[Kotlin] Generic (3) - 그 외

powered by pixabay

 

지난 Generic (1), (2) 포스팅에 이어서

Generic (3) 포스팅을 끝으로 제네릭에 대해 전부 알아보겠습니다


제네릭 타입 삭제 (Type Erasure)

kotlin은 런타임에 제네릭 타입 정보를 유지하지 않습니다.

→ 컴파일 시점에만 타입 정보가 사용되고 런타임에는 제네릭 타입에 대한 정보가 사라집니다.

 

is 연산자로 타입 체크를 할 수 없습니다.

fun <T> checkType(value: T) {
    if (value is String) {  // 오류: 'Cannot check for instance of erased type: T'
        println("This is a String!")
    } else {
        println("This is not a String!")
    }
}

fun main() {
    checkType("Hello")
    checkType(42)
}

 

대신 reified 타입 파라미터를 사용하는 인라인 함수로 이를 처리할 수 있습니다.

단, reified 타입은 인라인 함수에서만 사용할 수 있습니다.

inline fun <reified T> checkType(value: Any) {
    if (value is T) {
        println("This is a ${T::class.simpleName}!")
    } else {
        println("This is not a ${T::class.simpleName}!")
    }
}

fun main() {
    checkType<String>("Hello")  // 출력: This is a String!
    checkType<Int>("Hello")     // 출력: This is not a Int!
}

제네릭 확장 함수

제네릭 확장 함수는 타입에 구애 받지 않고, 확장 가능한 함수를 정의할 수 있습니다.

fun <T : Number> T.square(): T {
    return (this.toDouble() * this.toDouble()) as T
}

println(3.square())      // 출력: 9
println(2.5.square())    // 출력: 6.25
fun <T, R> T.convert(converter: (T) -> R): R {
    return converter(this)
}

// 사용 예
val length = "Hello".convert { it.length }
println(length) // 출력: 5

스타 프로젝션 (*)

제네릭 타입의 구체적인 타입을 알지 못해도, *를 사용해 모든 타입을 허용할 수 있습니다.

  • 구체적인 타입 인자를 알 수 없을 때 사용
  • 모든 가능한 타입을 수용하고 싶을 때 사용
fun printMap(map: Map<*, *>) {
    for ((key, value) in map) {
        println("Key: $key, Value: $value")
    }
}

val map = mapOf(1 to "One", 2 to "Two")
printMap(map)
// 출력:
// Key: 1, Value: One
// Key: 2, Value: Two

 

읽기 전용 (out)

스타 프로젝션에서 요소를 읽을 경우, 요소의 타입은 Any? 로 취급 됩니다.

이는 모든 타입의 상위 타입이므로 안전하게 쓸 수 있습니다.

fun readList(list: List<*>) {
    for (item in list) {
        println(item) // item의 타입은 Any?
    }
}

readList(listOf(1, 2, 3))      // 출력: 1, 2, 3
readList(listOf("A", "B", "C")) // 출력: A, B, C

 

쓰기 전용 (in)

스타 프로젝션을 사용하는 경우, 제네릭 타입에 요소를 추가하는 작업은 허용되지 않습니다.

타입 안정성을 보장하기 위함입니다.

fun writeToList(list: MutableList<*>) {
    list.add(1) // 오류
}


? extends

가끔 Java에서 제네릭 코드를 보면 <? extends> 이렇게 쓰인 곳이 있습니다.

하지만 Kotlin에서는 이러한 기능은 없으며,

이는 out 키워드가 동일한 역할을 수행합니다.

import java.util.List;

public class Main {
    public static void printNumbers(List<? extends Number> list) {
        for (Number number : list) {
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        List<Integer> integers = List.of(1, 2, 3);
        List<Double> doubles = List.of(1.1, 2.2, 3.3);

        printNumbers(integers); // 출력: 1, 2, 3
        printNumbers(doubles);  // 출력: 1.1, 2.2, 3.3
    }
}
fun printNumbers(list: List<out Number>) {
    for (number in list) {
        println(number)
    }
}

fun main() {
    val integers: List<Int> = listOf(1, 2, 3)
    val doubles: List<Double> = listOf(1.1, 2.2, 3.3)

    printNumbers(integers) // 출력: 1, 2, 3
    printNumbers(doubles)  // 출력: 1.1, 2.2, 3.3
}

 

'Kotlin' 카테고리의 다른 글

[Kotlin] Contract  (0) 2025.04.03
[Kotlin] Builder Pattern 대체하기  (0) 2025.01.13
[Kotlin] Generic (2) - 변성  (0) 2024.12.10
[Kotlin] Generic (1) - 제네릭?  (0) 2024.12.10
[Kotlin] 위임 (Delegation)  (2) 2024.11.26