클래스나 함수에서 사용하는 자료형을 외부에서 지정할 수 있는 기능인 제너릭
<제너릭 Generic>
: 함수나 클래스를 선언할 때 고정적인 자료형 대신 실제 자료형으로 대체되는 타입 파라미터를 받아 사용하는 방법
타입 파라미터에 특정 자료형이 할당되면 제너릭을 사용하는 모든 코드는 할당된 자료형으로 대체되어 컴파일 됩니다
ex) fun <Int> genericFunc (param: T): T
class GenericClass <Int> (var pref: T)
-> <> 안에 Int가 할당되었으므로 T 자리엔 모두 Int가 할당됨
따라서 캐스팅 연산 없이도 자료형을 그대로 사용할 수 있습니다
타입 파라미터의 이름은 클래스 이름과 규칙이 같지만 일반적으로 'Type'의 이니셜인 'T'를 사용하는 것이 관례이며
여러개의 제너릭을 사용할 경우 T의 다음 알파벳인 U와 V를 추가적으로 사용하기도 합니다
또한 제너릭을 특정한 수퍼 클래스를 상속받은 클래스 타입으로만 제한하려면
콜론(:)을 쓰고 수퍼클래스명을 쓰면 됩니다
ex) <T: SuperClass>
이렇게 선언된 제너릭은 어떻게 사용하는 걸까요?
함수에 제너릭을 선언한 경우
fun <Int> genericFunc (var param: Int) {}
genericFunc(1)
일반적인 함수처럼 사용하면 파라미터나 반환형을 통해 타입 파라미터를 자동으로 추론해줍니다
클래스에 제네릭을 선언한 경우
class GenericClass <T>
인스턴스를 만들 때 타입 파라미터를 수동으로 지정하거나 GenericClass<Int> ()
생성자에 제너릭이 사용된 경우 class GenericClass <T> (var pref: T)
지정하지 않아도 자동으로 추론됩니다 GenericClass(1) -> class GenericClass <Int> (var pref: Int)
실제 코드로 제네릭을 보도록 하겠습니다
fun main() {
UsingGeneric(A()).doShouting()
UsingGeneric(B()).doShouting()
UsingGeneric(C()).doShouting()
doShouting(B())
}
fun <T: A> doShouting (t:T) {
t.shout()
}
open class A {
open fun shout() {
println("A가 소리칩니다")
}
}
class B: A() {
override fun shout() {
println("B가 소리칩니다")
}
}
class C: A() {
override fun shout() {
println("C가 소리칩니다")
}
}
class UsingGeneric <T:A> (val t:T) { // 수퍼클래스를 A로 제한한 제너릭 T
fun doShouting() {
t.shout()
}
}
/* 출력
A가 소리칩니다
B가 소리칩니다
C가 소리칩니다
B가 소리칩니다
*/
우선 수퍼클래스로 사용될 open 된 클래스 A가 있습니다
클래스 A 안에는 open 된 함수 shout을 만들어줍니다
다음으론 A를 상속받은 클래스 B를 만들고 shout을 override 해줍니다
A를 상속받은 클래스 C 또한 shout을 override 해주었습니다
그리고 UsingGeneric 클래스를 만들되 수퍼클래스를 A로 제한한 제너릭 T를 선언하고
생성자에서는 제너릭 T에 맞는 인스턴스를 속성 t로 받도록 하였습니다
안에는 doShouting 함수를 만들어 t의 shout을 수행합니다
Main에서는 UsingGeneric 인스턴스를 만들되 파라미터로는 클래스 A의 인스턴스를 넘겨주고 doShouting()을 수행합니다
타입 파라미터를 수동으로 전달할 수도 있지만 ( UsingGeneric<A>(A()).doShouting() )
생성자의 패러미터를 통해 클래스 A라는 것을 알 수 있기 때문에 ( UsingGeneric(A()).doShouting() )
여기서는 생략이 가능합니다
그리고 클래스 B와 C도 같은 방법으로 UsingGeneric에 넘겨주고 출력 결과를 확인해보면
A, B, C 각각의 클래스의 shout 함수가 잘 실행된 것을 볼 수 있습니다
사실 제너릭을 사용하지 않고 UsingGeneric의 생성자에서 수퍼클래스인 A로 캐스팅하여 shout을 호출하여도 동작은 같겠지만
( class UsingGeneric (val t: A) )
제너릭을 사용하는 경우 사용할 때 Generic이 자료형을 대체하게 되어 캐스팅을 방지할 수 있으므로 성능을 더 높일 수 있습니다
Generic을 함수에 사용하기 위해 수퍼클래스를 A로 제한한 제너릭 T를 선언하고 doShouting 함수를 만들어주었습니다
패러미터로는 제너릭 T에 맞는 인스턴스 t를 받습니다
main에서는 doShouting 함수에 B의 인스턴스를 넘겨주었습니다
함수 역시 제너릭의 타입을 자동으로 추론하므로 별도로 타입 패러미터에 자료형을 전달할 필요는 없습니다
출력 결과를 보면 B가 소리칩니다 라고 출력되는 것을 확인할 수 있습니다
이것 역시 캐스팅 없이 B의 객체 그대로 함수에서 사용하는 것입니다
<제너릭 Generic> 포스팅은 여기서 마치도록 하겠습니다!
'Kotlin' 카테고리의 다른 글
[Kotlin] 클래스의 다형성 (0) | 2023.08.03 |
---|---|
[Kotlin] 익명객체와 옵저버 패턴 (0) | 2023.07.28 |
[Kotlin] 오브젝트 Object (0) | 2023.07.27 |
[Kotlin] 스코프 함수 (0) | 2023.07.26 |
[Kotlin] 고차함수와 람다함수 (0) | 2023.07.25 |