본문 바로가기
프로그래밍 놀이터/Kotlin

[도서 정리] Android Development with Kotlin - Generics Are Your Friends

by 돼지왕왕돼지 2018. 12. 15.

Android Development with Kotlin - Generics Are Your Friends




이 정리글은 Kotlin in Action 책을 보고 실무에 Kotlin 을 사용하던 사람이 몰랐던 내용이나 remind 하고 싶은 내용을 위주로 정리한 글입니다.

제대로 내용을 파악하시려면 책을 구매해서 읽어보세욤~


android with kotlin, co-variant, contra-variant, contravariant in, covariant out, declaration-site variance, generic type erase, generic type parameter convention, inline, invariant, jvm signature overload, jvm type easure, jvmname, kotlin array invariant, kotlin generic, kotlin invariant, kotlin list immutable, kotlin mutablelist invariant, lower bound wildcard, raw type collection, reified, reified inline, reified type parameter, subtyping relation, upper bound wildcard, use-site variance


-

Kotlin 에서 generics 는 기본적으로는 invariant 이다.

invariant 는 subtyping relation 이 없다는 것



-

generic 에서 subtyping 관계가 유지되는 것을 co-variant 라고 하고,

반대로 유지되는 것을 contra-variant.

그리고 유지되지 않는 것을 invariant 라고 한다.



-

Java 의 upper bound wildcard 설정은 co-variant 를 만들어주는데, 이를 Kotlin 에서는 out 으로 한다.

Java 의 lower bound wildcard 설정은 contra-variant 를 만들어주는데, 이를 Kotlin 에서는 in 으로 한다.



-

Use-site variance vs. declaration-site variance.


아래는 use-site variance 의 예

interface BaseView
interface ProductView: BaseView
class Presenter<T>

var presenter: Presenter<out BaseView> = Presenter<BaseView>()
var productPresenter = Presenter<ProductView>()
presenter = productPresenter


아래는 declaration-site variance 의 예

interface BaseView
interface ProductView: BaseView
class Presenter<out T>

var presenter = Presenter<BaseView>()
var productPresenter = Presenter<ProductView>()
presenter = productPresenter


-

Java 에서 array 는 co-variant 하다.

그래서 Object[] 를 요구하는 곳에 String[] 을 전달할 수 있다.


그러나 Kotlin 에서 array 는 invariant 하다.

Array<Any> 를 요구하는 곳에 Array<String> 을 전달할 수 없다.



-

Kotlin 에서의 List 는 immutable 하기 때문에 co-variant 이다.

MutableList 는 invariant 하다.



-

invariant 일 경우에는 generic 이 in(param), out(return) 어디든 쓰일 수 있다.

co-variant 일 경우네는 generic 이 out(return) 에만 쓰일 수 있고, contra-variant 일 경우에는 in(param) 에만 쓰일 수 있다.


그리고 이 position restriction 은 private 인 경우에는 적용되지 않는다.



-

constructor 의 param 의 경우는 항상 invariant 로 사용된다.

즉, in, out position 에 상관 없다는 이야기이다.


이렇게 한 이유는 constructor 는 생성시 한번만 불리고 그 이후로는 불리지 않음이 guarantee 되기 때문이다.

단, variance modifier 를 넣었을 때 public 이면 val 만 가능하고, private 이면 var 도 가능하다.



-

generic type erase 때문에 같은 JVM signature overload 가 불가능하다

fun sum(ints: List<Int>){
    ...
}

fun sum(ints: List<Long>){
    ...
}

// compile error

위의 코드를 수정하려면... 

위쪽의 @JvmName(“intSum”) 과 같이 annotation 을 붙여주면 된다.

Kotlin 에서는 동일하게 그냥 sum 으로 부를 수 있지만, Java 에서 호출할대는 intSum 으로 호출해야 한다...



-

Reified type parameter

fun <T> typeCheck(s: Any){
    if(s is T){
        ...
    }else{
        ...
    }
}

위 코드는 compile error 가 난다.

이유는 T 가 erase 되기 때문이다.


이를 방지하기 위해.. 즉 type erase 를 안 되게 하려면 reified 를 넣어줘야 한다.

그리고 reified 는 inline 에서만 작동한다. ( inline 에서만 작동하는 이유는 실제 inline 되면서. T 를 실제 class 로 치환하기 때문, JVM 의 기본특성인 type erasure 자체를 거스를 순 없다. )


inline fun <reified T> typeCheck(s: Any){
    ...
}



-

다음과 같이 reified type 에 대해 reflection 도 사용할 수 있다.

inline fun <reified T> isOpen(): Boolean {
    return T::class.isOpen
}



-

Kotlin 에서는 raw type collection 을 정의할 수 없다.

SimpleList<> // compile error
SimpleList<*> // ok, called star-projection



-

val anyBox: Box<Any> = Box<Int> // Error: Type mismatch
val anyBox: Box<*> = Box<Int> // ok.



-

Generic type parameter convention.


E : Element

K : Key

N : Number

T : Type

V : Value

S, U, V, and so on

T, R : Type, Return Type




댓글0