Generic

sumi Yoo·2022년 11월 11일
0

Generic

다양한 타입에서 사용 가능한 코드를 작성할 수 있다.

Invariance(무공변성)

다음 코드는 컴파일 에러가 발생합니다. Double이 Number를 상속한다고 해서 List<Double>이 List<Number>를 상속한다고 말할 수 없기 때문입니다.

List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
List<Number> numbers = doubles; // compile error

클래스의 상속관계가 Generics에서는 상속관계로 유지되지 않는 것을 Invariance라고 합니다.
List<Double>이 List<Number>를 상속하지 않는 이유는 Generics는 컴파일 단계에서 Generics의 타입이 지워지기 때문입니다. JVM은 Runtime에 List 객체만 알고 있고, Generics의 타입이 Double인지, Number인지 알지 못합니다.

Covariance(공변성)

클래스의 상속관계가 Generics에서도 상속관계가 유지되는 것을 말합니다.

List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
List<Number> numbers = doubles; // ok

하지만, Covariance를 이용하여 객체를 할당하면, 객체를 read할 수 있지만 write는 어렵습니다.
get()은 문제없지만 add()는 컴파일에러가 발생합니다

Number number = numbers.get(0);
System.out.println(number);
numbers.add(1.1); // compile error

get()으로 리턴된 객체는 Number를 상속하는 Long, Integer, Dobule 중에 하나이고, Number로 할당받을 수 있습니다.

하지만, add()는 조금 다릅니다. 다음과 같이 List의 타입이 정해져있지 않아서 add()로 특정 클래스의 객체를 추가할 수 없습니다.

// compile error, List<>의 타입이 Double인지 알 수 없음
numbers.add(1.1);
// compile error, List<>의 타입이 Integer인지 알 수 없음
numbers.add(1);
// compile error, List<>의 타입이 Long인지 알 수 없음
numbers.add(1L);

Contravariance(반공변성)

클래스의 상속관계가 Generics에서는 반대인 것을 것을 의미합니다.

Reified

Reified 키워드는 Generics로 inline function에서 사용되며, Runtime에 타입 정보를 알고 싶을 때 사용합니다.

아래 코드는 컴파일 에러가 발생합니다. Runtime에 타입 정보가 지워지기 때문입니다.

fun <T> printGenerics(value: T) {
    when (value::class) {  // compile error!
        String::class.java -> {
            println("String : $value")
        }
        Int::class.java -> {
            println("Integer : $value")
        }
    }
}

이럴 때 타입 정보가 있는 객체를 인자로 전달하면 문제를 해결할 수 있습니다.

아래와 같이 Class<T>로 인자를 전달하였고 이것으로 타입을 알 수 있습니다.

fun <T> printGenerics(value: T, classType: Class<T>) {
    when (classType) {
        String::class.java -> {
            println("String : $value")
        }
        Int::class.java -> {
            println("Int : $value")
        }
    }
}

printGenerics("print generics function", String::class.java)
printGenerics(1000, Int::class.java)

Reified 키워드를 사용하여 타입 정보 얻기

reified 키워드를 사용했기 때문에 T::class처럼 클래스의 타입 정보를 얻을 수 있습니다. 그래서 타입 정보를 인자로 넘기지 않아도 Runtime에 타입 정보를 알 수 있습니다.

inline fun <reified T> printGenerics(value: T) {
    when (T::class) {
        String::class -> {
            println("String : $value")
        }
        Int::class -> {
            println("Int : $value")
        }
    }
}

printGenerics1("print generics function")
printGenerics1(1000)

함수 오버로딩

타입에 따라 리턴되는 타입이 다르도록 구현되었습니다.

inline fun<reified T> getMessage(number: Int): T {
    return when (T::class) {
        String::class -> "The number is : $number" as T
        Int::class -> number as T
        else -> "Not string, Not Integer" as T
    }
}

위의 함수는 아래와 같이 사용할 수 있습니다. 리턴되는 타입에 따라서 T가 결정되며 T에 따라서 리턴되는 객체가 달라집니다.

val result : Int = getMessage(10)
println("result: $result")

val resultString : String = getMessage(100)
println("result: $resultString")

https://codechacha.com/ko/java-covariance-and-contravariance/
https://codechacha.com/ko/kotlin-reified-keyword/#reified-%ED%82%A4%EC%9B%8C%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%ED%83%80%EC%9E%85-%EC%A0%95%EB%B3%B4-%EC%96%BB%EA%B8%B0
https://readystory.tistory.com/201

0개의 댓글

관련 채용 정보