Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20241106 제네릭(Generic) 본문

Kotlin

[TIL]20241106 제네릭(Generic)

최밤빵 2024. 11. 6. 22:38

제네릭은 코드의 타입 안정성과 재사용성을 높이는 중요한 개념이다. 자바에서 제네릭을 사용해본 경험이 있지만, 코틀린에서의 제네릭은 몇 가지 차이점과 함께 보다 직관적인 문법을 제공한다. 

 

▶제네릭(Generic) 개념 이해

제네릭은 데이터의 타입을 일반화해 코드를 보다 유연하게 작성할 수 있는 방법으로 다양한 타입에 대해 하나의 코드 블록으로 처리할 수 있어 코드의 중복을 줄이고 유지보수성을 높인다. 제네릭은 주로 클래스나 함수에서 사용되며, 실행 시점이 아닌 컴파일 시점에 타입을 체크하여 타입 안정성을 확보할 수 있다.


▶제네릭 클래스 작성

제네릭 클래스를 선언할 때는 클래스 이름 뒤에 꺽쇠(<>) 안에 타입 매개변수를 지정한다. 

class Box<T>(val content: T) {
    fun getContent(): T {
        return content
    }
}

fun main() {
    val intBox = Box(123)
    val stringBox = Box("Hello, Kotlin")

    println("IntBox contains: ${intBox.getContent()}")
    println("StringBox contains: ${stringBox.getContent()}")
}

→  Box<T>는 제네릭 클래스로, T는 타입 매개변수이다. intBoxstringBox는 각각 IntString 타입의 데이터를 저장하는 Box 객체이다. T는 사용자가 지정한 타입으로 대체되어 코드의 재사용성을 높이고, 컴파일 시점에 타입이 체크되어 타입 안정성을 보장한다.


▶제네릭 함수 작성

제네릭은 함수에서도 사용할 수 있다. 제네릭 함수를 작성할 때는 함수 이름 앞에 타입 매개변수를 꺽쇠(<>) 안에 정의한다. 

fun <T> printItem(item: T) {
    println("Item: $item")
}

fun main() {
    printItem(42)
    printItem("Hello, World!")
    printItem(3.14)
}

→ printItem 함수는 타입 매개변수 T를 받아서 다양한 타입의 데이터를 출력할 수 있다. 이로 인해 함수는 Int, String, Double 등 어떤 타입이든 받을 수 있고, 호출 시점에 인자의 타입에 맞춰 T가 대체된다.


▶제네릭의 타입 제한 (Type Bound)

코틀린의 제네릭은 타입 제한(Type Bound)을 통해 특정 타입이나 타입 계층에 제약을 걸 수 있다. 이를 통해 함수나 클래스에서 사용할 타입의 범위를 제한하여 더욱 안전한 코드를 작성할 수 있다. 타입 제한을 적용하려면 T: SuperType과 같이 작성한다.

fun <T : Number> sumValues(a: T, b: T): Double {
    return a.toDouble() + b.toDouble()
}

fun main() {
    println(sumValues(10, 20))
    println(sumValues(15.5, 4.5))
    // println(sumValues("Hello", "World")) // 오류 발생: T는 Number 타입이어야 함
}

→ sumValues 함수는 타입 매개변수 TNumber 타입의 제한을 두어 Int, Double, FloatNumber의 서브 타입만 인자로 받을 수 있게 했다. 따라서 타입 안정성을 더욱 높일 수 있다.


▶코틀린의 제네릭과 자바의 제네릭 비교

자바에서도 제네릭을 사용하지만, 코틀린의 제네릭은 조금 더 직관적이고 간단한 문법을 제공한다. 코틀린에서는 제네릭 타입 매개변수에 공변성과 반공변성을 명시적으로 설정할 수 있으며, inout 키워드를 사용해 제네릭의 타입 변성을 제어한다. 자바에서는 ? extends? super를 사용해 와일드카드로 변성을 처리하지만, 이는 가독성이 떨어지고 이해하기 어렵다.

▷코틀린 

class Container<out T>(val item: T)

fun main() {
    val stringContainer: Container<String> = Container("Kotlin")
    val anyContainer: Container<Any> = stringContainer // 공변성(out) 덕분에 가능
}

 

▷자바

class Container<T> {
    private T item;

    public Container(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

public static void main(String[] args) {
    Container<String> stringContainer = new Container<>("Java");
    Container<? extends Object> anyContainer = stringContainer; // 와일드카드 사용
}
코틀린의 out 키워드는 자바의 ? extends와 비슷하지만 더 읽기 쉬운 문법을 제공한다.

제네릭은 타입 안정성을 높이고, 코드의 재사용성을 극대화할 수 있는 강력한 기능이다. 코틀린에서는 제네릭 클래스와 함수를 통해 다양한 타입을 다룰 수 있으며, 타입 제한과 변성을 통해 더 안전한 코드를 작성할 수 있다. 자바와 비교했을 때 코틀린의 제네릭은 간결하고 직관적인 문법을 제공하며, 이를 통해 가독성이 높은 코드를 작성할 수 있다. 

'Kotlin' 카테고리의 다른 글

[TIL]20241108 Lazy  (1) 2024.11.08
[TIL]20241107 실드클래스(Sealed Class)  (1) 2024.11.07
[TIL]20241105 Label & break/continue  (1) 2024.11.05
[TIL]20241104 Named Argument  (1) 2024.11.04
[TIL]20241103 Immutable Collection & Mutable Collection  (6) 2024.11.03