제네릭과 배열 (2/3)

20293 단어 TILTIL_KotlinTIL

Do it! 코틀린 프로그래밍 [셋째마당, 코틀린 표준 라이브러리의 활용] 학습

1-3. 자료형 제한하기

  • 콜론(:)과 자료형을 기입하여 형식 매개변수의 자료형을 제한

📌 클래스에서 형식 매개변수의 자료형 제한

class Calc<T: Number> {
    fun plus(arg1: T, arg2: T): Double {
        return arg1.toDouble() + arg2.toDouble()
    }
}

fun main() {
    val calc = Calc<Int>()
    
    val calc2 = Clac<String>() // !오류!
}

-> 형식 매개변수 T를 Number형으로만 제한해두었기 때문에 객체를 생성할 때 String과 같이 Number형이 아닌 자료형이 지정되면 오류 발생

📌 함수에서 형식 매개변수의 자료형 제한

fun <T : Number> addLimit(a: T, b: T, op: (T, T) -> T): T {
    return op(a, b)
}

val result = addLimit("abc", "def", { a, b -> a + b }) // !오류!

-> 함수 이름 앞에 형식 매개변수를 Number형으로만 제한하여 문자열 처리 불가

📌 다수 조건의 형식 매개변수 제한

  • where키워드를 통해 지정된 제한을 모두 포함하는 경우만 허용하도록

⬇️ 클래스에서 where 사용

interface InterfaceA
interface InterfaceB

class HandlerA: InterfaceA, InterfaceB
class HandlerB: InterfaceA

class ClassA<T> where T:InterfaceA, T:InterfaceB

fun main() {
    val obj1 = ClassA<HandlerA>()
    val obj2 = ClassA<HandlerB>() // !오류!
}

-> InterfaceA만 구현하는 HandlerB는 where범위에 들어갈 수 없음

⬇️ 함수에서 where 사용

fun <T> myMax(a: T, b: T): T where T:Number, T:Comparable<T> {
    return if (a > b) a else b
}

-> a, b에 들어갈 자료형을 숫자형과 비교형으로 한정


1-4. 상·하위 형식의 가변성

  • 가변성(Variance) : 형식 매개변수가 클래스 계층에 영향을 주는것

📌 클래스와 자료형

📌 가변성의 3가지 유형

용어의미
공변성(Covariance)T'가 T의 하위 자료형이면, C<T'>C<T>의 하위 자료형이다.
생산자 입장의 out 성질
반공변성(Contravariance)T'가 T의 하위 자료형이면, C<T>C<T'>의 하위 자료형이다.
생산자 입장의 in 성질
무변성(Invariance)C<T'>C<T>는 아무 관계가 없다.
생산자 + 소비자

📌 무변성 (Invariance)

  • 형식 매개변수에 공변성이나 반공변성을 따로 지정하지 않으면 무변성으로 제네릭 클래스가 선언됨
class Box<T>(val size: Int)

fun main() {
    val anys: Box<Any> = Box<Int>(10) // !오류! 자료형 불일치
    val nothings: Box<Nothing> = Box<Int>(20) // !오류! 자료형 불일치
}

-> 제네릭 클래스가 무변성으로 선언되었을 경우, 클래스의 상하관계를 올바르게 따졌어도 자료형 불일치 오류가 발생

📌 공변성 (Covariance)

  • 형식 매개변수의 상하 자료형 관계가 성립하고, 그 관계가 그대로 인스턴스 자료형 관계로 이어지는 경우
  • IntAny 의 하위 자료형일 때 형식 매개변수 T에 대해 공변적이라고 함
  • 형식 매개변수를 갖는 프로퍼티는 val만 허용
    • var를 사용하려면 private으로 지정해야함
  • out 키워드 사용
// 공변성 선언
class Box<out T>(val size: Int)

fun main() {
	val anys: Box<Any> = Box<Int>(10) // 객체 생성 가능
    val nothings: Box<Nothing> = Box<Int>(20) // !오류! 자료형 불일치
}

-> Any의 하위 클래스인 Int는 공변성을 가지므로 자료형 할당 가능
-> Nothing은 Int의 하위 자료형이 아니므로 오류 발생

📌 반공변성 (Contravariance)

  • 자료형의 상하관계가 반대가 되어 인스턴스의 자료형이 상위 자료형이 됨
  • in 키워드 사용
// 반공변성 선언
class Box<in T>(val size: Int)

fun main() {
	val anys: Box<Any> = Box<Int>(10) // !오류! 자료형 불일치
    val nothings: Box<Nothing> = Box<Int>(20) // 객체 생성 가능
}

-> Box<Noting> 자료형의 상위 자료형이 Box<Int> 이므로 객체를 생성할 수 있음


1-5. 자료형 프로젝션

📌 선언 지점 변성(declaration-site variance)

  • 클래스를 선언하면서 클래스 자체에 가변성을 지정하는 방식
  • 클래스를 사용하는 장소에서는 따로 자료형을 지정해 줄 필요 없음

📌 사용 지점 변성(use-site variance)

  • 메서드 배개변수에서 또는 제네릭 클래스를 생성할 때와 같이 사용 위치에서 가변성을 지정하는 방식
  • 형식 매개변수가 있는 자료형을 사용할 때마다 하위 자료형이나 상위 자료형 중 어떤 자료형으로 대체할 수 있는지 명시 필요

📌 스타 프로젝션

  • in/out 을 정하지 않고 스타(*)를 통해 지정하는 방법
  • 구체적으로 자료형이 결정되고 난 후 그 자료형과 하위 자료형의 요소만 담을 수 있도록 제한
class InOutTest<in T, out U>(t: T, u: U) {
	val propT: T = t // !오류! T는 in 위치이기 때문에, out 위치에 사용 불가
    val propU: U = u // U는 out 위치로 가능
    
    fun func1(u: U) // !오류! U는 out 위치이기 때문에 in 위치에 사용 불가
    fun func2(t: T) { } // T는 in위치에 사용됨
}

fun startTestFunc(v: InOutTest<*, *>) {
	v.func2(1) // !오류! Nothing으로 인자를 처리
    print(v.propU)
}

-> in으로 정의되어 있는 형식 매개변수를 로 받으면 in Nothing으로 간주
-> out으로 정의되어 있는 형식 매배경수를
로 받으면 out Any?로 간주


1-6. reified 자료형

fun <T> myGenericFun(c: Class<T>)

  • T자료형은 자바처럼 실행 시간에 삭제되기 때문에 T자체에 그대로 접근 불가능
  • <T> 처럼 결정되지 않은 제네릭 자료형은 컴파일 시간에는 접근 가능하나 함수 내부에서 사용하려면 c: Class<T>처럼 함수의 매개변수에 넣어 지정해야만 실행시간에 사라지지 않고 접근 가능

inline fun <reified T> myGenericFun()

  • reified로 형식 매개변수 T를 지정하면 실행 시간에 접근 가능
  • reified자료형은 인라인 함수에서만 사용 할 수 있음
fun main() {
    val result = getType<Int>(10)
    println("result = $result")
}

inline fun <reified T> getType(value: Int): T {
    println(T::class) // T는 실행 시간에 삭제되지 않고 사용 가능
    println(T::class.java)

    return when(T::class) { // 받아들인 제네릭 자료형에 따라 반환
        Float::class -> value.toFloat() as T
        Int::class -> value as T
        else -> throw IllegalStateException("${T::class} is not supported!")
    }
}

좋은 웹페이지 즐겨찾기