본문 바로가기
Java & Kolin

Kotlin의 Generic - 기본문법

by Limm_jk 2021. 3. 3.

제네릭은 클래스 내부에서 사용할 자료형을 나중에 생성할 때 결정하도록 도와줍니다.

제네릭을 사용함으로서 자료형의 안정성이 높아지고, 형 변환의 번거로움이 줄어듭니다.

또한, 이후 컬랙션의 사용에도 큰 도움이 됩니다.

 

자바와 비슷하게 코틀린에서도 제네릭을 사용합니다.

<>사이에 매개변수를 넣는 방식으로 동일하게 사용합니다.

 

간단한 예시로 한번 살펴보겠습니다.

class Box<T>(arg:T){
    var name = arg
}

fun main() {
    val box1:Box<Int> = Box<Int>(1)
    val box2:Box<String> = Box<String>("box")
    println("${box1.name} ${box2.name}")
}

/*
Output
1 box
*/

위와 같이 타입을 T로 지정해주고, 이후에 T에 원하는 자료형을 붙여서 사용할 수 있습니다.

이 때, T는 foo bar 같은 많이 쓰이는 이름일 뿐 강제되지 않습니다.

 

또한 main에서 변수 선언 시 Box<Int>와 같이 타입지정을 해주지 않아도 알아서 추론해서 캐스팅해줍니다.

 

제네릭 함수 / 메서드

타입을 매개변수로 받는 함수 / 메서드를 제네릭 함수 / 메서드라고 부릅니다. 

fun <타입 매개변수, ...> 함수 이름(매개변수 : <매개변수 자료형>):<반환 자료형>

위와 같은 방식으로 사용할 수 있으며, 타입 매개변수를 여러 개를 사용하기도 합니다.

fun<T> find(a: Array<T>, Target: T):Int{
    for(i in a.indices){
        if(a[i] == Target) return i;
    }
    return -1
}

fun main() {
    var arr1: Array<String> = arrayOf("apple","bananan","cherry","grape")
    var arr2:Array<Int> = arrayOf(1,2,3,4)

    println("범위는 ${arr1.indices}")
    println("범위는 ${arr2.indices}")
    println(find<String>(arr1, "cherry"))
    println(find(arr2,2))
}

위의 코드와 같이 사용할 수 있으며, 마지막의 find(arr2,2)와 같이 <>타입 선언을 빼도 정상 작동한다.

 

제네릭 함수에서의 연산은 자료형을 결정할 수 없기 때문에 오류가 발생한다. 이런 오류를 해결하기 위하여 람다식을 매개변수로 받아서 연산을 진행한다.

fun<T> add(a:T, b:T, op:(T,T)->T):T{
    return op(a,b)
}
fun main() {
    val res = add(2,3,{a,b -> a+b})
    println(res)
}

제네릭에서 +연산을 진행할 때, +연산에 정의된 자료형이 없기에 오류가 발생하나, 람다식을 이용하여 자료형과 함께 넘겨주는 방식을 이용하면 해결 할 수 있습니다.

 

또한 위의 코드는 가독성을 올리기 위하여 아래와 같이 변경할 수 있습니다.

typealias arith<T> = (T,T)->T

fun<T> add(a:T, b:T, op:arith<T>):T{
    return op(a,b)
}
fun main() {
    val res = add(2,3,{a,b -> a+b})
    println(res)
}

이외에 람다식을 따로 정의하여 가독성을 높여줄 수 있습니다. 

자료형 제한

타입 매개변수 '<T>' 에 자료형을 제한할 수 있습니다.

<T:Number>와 같이 특정한 자료형을 붙여서 제한하는 방법이 있습니다.

 

위에서 사용한 코드를 이용하여 예시를 보겠습니다.

typealias arith<T> = (T,T)->T

fun<T:Number> add(a:T, b:T, op:arith<T>):T{
    return op(a,b)
}
fun main() {
    val res = add(2,3,{a,b -> a+b})
    println(res)
    val res1 = add("23","34",{a,b -> a+b})
    println(res1)
}

아래에 res1을 만들고 String타입을 연산하는 함수를 만들어줬습니다.

여기서 <T>였다면 정상작동을 하지만, <T:Number>로 Number형으로 제한하여 String 타입의 연산인 res1은 오류가 발생합니다.

댓글