Generic

제네릭은 인자로 사용하는 타입에 따라 구체화되는 클래스나 인터페이스를 의미한다.

코틀린에서도 자바와 동일하게 <>로 제네릭을 사용한다.

//java
List<String> names;
Map<String, String> entries;
//kotlin
val names: List<String>
val entries: Map<String, String>

제네릭클래스에 타입을 넣지 않고 선언이 가능한 자바와 달리, 코틀린은 반드시 타입을 넣어 주어야 한다. -> 컴파일에러

//java
//List<Object>로 암시적으로 선언됨.
List names;
//kotlin
//컴파일 에러
val names: List

제네릭을 사용하는 클래스나 인터페이스를 정의하는 방법도 자바와 동일하다.

//java
class Car {
    ...
}

//항목을 담거나 뺄 수 있는 지네릭 인터페이스 정의
interface Container<T> {
    void put(T item);
    T take();
}

class Garage implements Container<Car> {
    @Override
    public void put(Car item) {
        ...
    }

    @Override
    public Car take() {
        ...
    }
}
//kotlin
class Car {
    ...
}

interface Container<T> {
    fun put(item: T)
    fun take(): T
}

class Garage: Container<Car> {
    override fun put(item: Car) {
        ...
    }
    
    override fun take(): Car {
        ...
    }
}

인자로 받을 수 있는 타입을 한정하는 방법 또한 동일하다.

//java
interface Container<T extends Car> {
    void put(T item);

    T take();
}
//kotlin
interface Container<T: Car> {
    fun put(T item)

    fun take(): T
}

타입이 정의되어 있는 제네릭을 인자로 받거나 호출시점에 타입을 지정하는 함수는 자바와 동일한 방법으로 정의한다. 단, 호출시점에 타입을 정의하는 함수는 타입정의 위치가 자바와 약간 다르다.

//java
//이미 타입이 정해진 제네릭을 인자로 받는 예
void processItems(List<String> items) {
    ...
}

//호출시점에 타입이 정해지는 제네릭을 인자로 받는 예
public <T> void processItems(List<T> items) {
    List<T> datas = new ArrayList<>(items);
}
//kotiln
//이미 타입이 정해진 제네릭을 인자로 받는 예
fun processItems(items: List<Strings>) {
    ...
}

//호출시점에 타입이 정해지는 제네릭을 인자로 받는 예
fun <T> processItems(items: List<T>) {
    ...
}

호출시점에 타입이 정해지는 제네릭을 인자로 받는경우, 정해지는 타입 및 그 하위 타입을 받도록 지정하거나 (upper bound) 정해지는 타입 및 그 상위 타입을 받도록 (lower bound) 지정할 수 있다.

자바에서의 ? super T, ? extends T는 각각 코틀린에서 in T, out T로 사용한다.

class Car { ... }

class Sedan extends Car { ... }

class Truck extends Car { ... }

//dst로 받은 목록을 dest에 추가한다.
<T> void append(List<? super T> dest, List<? extends T> dst) {
    dest.addAll(dst);
}

//사용 예
List<Sedan> sedans = ...;

List<Truck> trucks = ...;

List<Car> cars = ...;

append(cars, sedans);

append(cars, trucks);
//kotlin
open class Car { ... }

class Sedan: Car() { ... }

class Truck: Car() { ... }

fun <T> append(dest: MutableList<in T>, src: List<out T>) {
    dest.addAll(dst)
}

//사용 예
val sedans: List<Sedan> = ...

val trucks: List<Truck> = ...

val cars: MutableList<Car> = ...

append(cars, sedans)

append(cars, trucks)

Last updated