반응형

 코틀린의 클래스에서는 자바처럼 type parameter를 가질 수 있다.

class Box<T>(t: T) {
    var value = t
}

 Box Class의 instance를 만들기 위해서 아래와 같이 Type을 꺽쇠부호 사이에 넣어주면 된다.

val box: Box<Int> = Box<Int>(1)

 하지만 위와같은 경우 1이 Int이므로 컴파일러가 유추할 수 있어 아래와 같이 생략이 가능하다.

val box: Box = Box(1)

 그런데 : Box 이 부분도 명시적으로 선언하지 않아도 어차피 Box class를 생성하는 것이기 때문에 생략할 수 있다.

 그래서 코드는 아래와 같아진다.

val box = Box(1)

 다시 제네릭으로 돌아와서, 그렇다면 이 제네릭이라는 친구를 언제 쓸까?

 

 대학교 때 교수님의 강의를 떠올려보면, 교수님께서는 C에서 두 수의 합을 구하는 프로그램을 예로 들었었다.

int sum(int a, int b){ return a+b; } 이런식의 코드가 있을 때, int가 아닌 float, double 형식으로 만들려면 함수를 세 개나 만들어야 한다. float로 만들고 싶다면, float sum(float a, float b){ return a+b; } 이런식으로 추가적으로 만들어줘야 한다. 별다른 의미 없이 같은 코드가 반복되고 있기 때문에 비효율적이다.

 

더보기

int sum(int a, int b){ return a+b; }을 float sum(float a, float b){ return a+b; } 이런식으로 또 다르게 표현해주는 것은 오버로딩일까?

-> 맞다. 오버로딩의 조건은 아래 두 개와 같다.

 

출처 위키피디아

 

1. 메서드의 이름이 같아야 한다.

2. 매개변수의 개수가 다르거나, 매개변수의 타입이 달라야 한다.

int sum()과 float sum()은 메서드의 이름이 같고, 두 메서드는 매개변수의 개수는 같지만 타입이 달라 구분이 가능하기 때문이다.

 

 

 이처럼 여러 자료형이 필요하다면 그 자료형에 대해서 모두 다 만들어주어야 하는데, 그 귀찮음을 Generic을 통해 줄여줄 수 있다.

 

 여러 제네릭 중 코틀린에서 sort() 함수에서 이용하고 있는 제네릭을 확인해보자. 

확인하기에 앞서서 sort()는 비교할 수 있는 값들을 비교하여 정렬을 해주는 함수다. 숫자를 정렬할 수도 있고, 문자를 정렬할 수도 있고, 문자열을 정렬할 수도 있다. 즉 한 가지의 데이터 형식이 아닌, 여러가지의 데이터 형식을 정렬할 수 있는 아주 유용하고 자주 사용되는 함수다.

 

 이 sort 함수의 내부는 아래와 같이 되어 있다.

public actual fun <T : Comparable<T>> MutableList<T>.sort(): Unit {
    if (size > 1) java.util.Collections.sort(this)
}

 <T : Comparable<T>> 부분에서 제네릭을 사용할 것이라고 말을 했다. 좀 더 고급스러운 표현을 사용한다면, "타입 파라미터를 선언했다"라고 할 수 있다. 이 타입 파라미터(T)는 수신 객체와 반환 타입에 사용된다. 

 

 <T> 이렇게만 선언해도 제네릭을 이용할 수 있는데, 뒤에 Comparable<T> 코드를 추가한 이유는 비교가 가능한 타입이여야 하기 때문이다. 예를 들어 비교가 불가능한(comaparable을 설정하지 않은) 클래스가 들어온다면, 정렬을 할 수 없다. 그렇기 때문에 Comparable 타입 상한을 지정해주는 것이다. "타입 상한"은 다시 말해서 한정적으로 특정 타입을 사용할 수 있다고 선언해두는 것이다. 위의 sort함수같은 경우는 비교가 가능한 클래스만 들어올 수 있게 : Comparable<T> 코드를 통해 상한하고 있다.

 

반응형

+ Recent posts