Intro

처음 프로그래밍 언어를 배웠을 때, 처음 벽을 느끼게 하는 건 항상 제네릭이었다.

하지만 지금은 이론을 떠나서 실제 코딩을 통해 제네릭에 대해 어느 정도 이해가 된 상태이기에, 이 기회를 놓치지 말고 제네릭을 격파시키자!!

제네릭(Generics)

제네릭이란?

제네릭이란 영어로 ‘일반적인’이라는 뜻이다.

프로그래밍에서 제네릭이란 타입을 두루뭉실?하게 선언하여 보다 일반적인 객체(혹은 메서드나 프로퍼티)를 만드는 것을 의미하는데, 보다 직관적으로 설명하자면 ‘객체의 타입을 호출하는 외부에서 정할 수 있게 하는 기법’이다.

제네릭 타입 파라미터를 사용하여, 제네릭 클래스, 함수, 확장 프로퍼티를 정의할 수 있다.

(일반 프로퍼티는 타입 파라미터를 가질 수 없다 → 애초에 여러 타입을 가지는 것이 말이 안됨)

제네릭 타입 파라미터 <T>

제네릭 함수

아래의 제네릭 함수를 통해 제네릭 타입 파라미터에 대해 알아보자.

함수 이름 앞에 <> 기호를 통해 타입 파라미터 T를 사용하는 제네릭 함수임을 나타내고 있다.

fun <T> List<T>.slice(indices: IntRange): List<T>

위의 함수를 호출할 때 타입 파라미터의 타입을 결정해 주어야 한다.

println(letters.slice<Char>(0..2))

하지만 다른 타입들처럼 컴파일러가 타입을 추론할 수 있는 경우, 생략 가능하다.

제네릭 클래스

제네릭 클래스의 예시는 다음과 같다.

interface List<T> {
    operator fun get(index: Int): T {
        ...
    }
}

제네릭 클래스를 확장하는 클래스를 정의하려면 기반 타입의 제네릭 파라미터에 대한 타입 인자를 지정해야 하는데, 이때, 구체적인 타입을 넘길 수도 있고 (하위 클래스도 제네릭 클래스라면) 타입 파라미터로 받은 타입을 그대로 넘길 수도 있다.