티스토리 뷰

Android/Kotlin

[Kotlin] Generic

DevJunku 2021. 12. 7. 13:18

Generic: 클래스나 함수에서 사용하는 자료형을 외부에서 지정할 수 있는 기능

class A와 A를 상속받은 class B가 있다고 해보자.

이때 두 클래스의 인스턴스를 공용으로 사용하는 하나의 함수에 파라미터로 받으려면 어떻게 해야할까?

물론 다형성의 개념을 활용하여 캐스팅한 변수를 함수의 인자로 넣어줄 수 있는 방법이 존재하지만, 이는 프로그램의 속도를 저하시킬 수 있는 문제점을 앉고 있다.

그래서 Generic이라는 개념이 나왔다.

Generic이라는 개념을 다시 이야기하자면, 함수나 클래스를 선언할 때 고정적인 자료형 대신 실제 자료형으로 대체되는 타입 패러미터를 받아서 사용하는 방법이다. 다음과 같이 할 수 있다.

fun <Int> genericFunc(Param: T): T
class GenericClass <Int> (var pref: T)

T는 type을 의미하는데, 여기서는 Int로 지정했으니 알아서 Int로 모두 변환되어 컴파일된다. 따라서 casting 연산없이도 자료형을 사용할 수 있다.

Generic을 특정한 super class를 상속받은 클래스 타입으로만 제한하려면, <T: SuperClass>로 사용 가능하다. 실제로 Generic을 사용해보자.

📝 Generic을 클래스에서 사용하는 방법

fun main(args: Array<String>) {
    UsingGeneric(A()).sleeping() // A가 잡니다
    UsingGeneric(B()).sleeping() // B가 잡니다
    UsingGeneric(B()).sleeping() // C가 잡니다
}

open class A {
    open fun sleep() {
        println("A가 잡니다")
    }
}

class B: A() {
    override fun sleep() {
        println("B가 잡니다")
    }
}

class C: A() {
    override fun sleep() {
        println("C가 잡니다")
    }
}

class UsingGeneric<T: A> (val t: T) { // 제네릭 class임 T로 특정 클래스 타입을 지정하지 않았음
    fun sleeping() {
        t.sleep()
    }
}

물론 위 코드에서 Generic을 사용하지 않고 UsingGeneric의 생성자에서 class UsingGeneric(val t: A)로 호출하여도 동작은 같겠지만, 위와 같이 Generic을 사용하면, 사용할 때 UsingGeneric이 자료형을 대체하게 되어 캐스팅을 방지할 수 있다. 그렇기 때문에 성능을 더 높일 수 있다.

📝 Generic을 함수에서 사용하는 방법

fun main(args: Array<String>) {
    sleeping(A()) // A가 접니다.
    sleeping(B()) // B가 접니다.
    sleeping(C()) // C가 접니다.
}

fun <T:A> sleeping(t: T) {
    t.sleep()
}

open class A {
    open fun sleep() {
        println("A가 잡니다")
    }
}

class B: A() {
    override fun sleep() {
        println("B가 잡니다")
    }
}

class C: A() {
    override fun sleep() {
        println("C가 잡니다")
    }
}

이 역시 캐스팅 없이 함수에서 그대로 사용하는 것을 알 수 있다