KOTLIN

[KOTLIN] 제네릭 타입과 VARIANCE 한정자를 활용하라 (이펙티브 코틀린)

집한구석 2022. 7. 10. 11:21
728x90

제네릭 요약 참고

 

[JAVA] 제네릭

제네릭 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법 외부에서 사용자에 의해 타입이 지정되는 것을 의미 제네릭 장점 잘못된 타입이 들어올 수 있는 것을

tgyun615.com


Variance 한정자 (out / in)

class Box<out T>

fun main(args: Array<String>) {
  val box1: Box<Number> = Box<Int>() // OK!!
  val box2: Box<Int> = Box<Number>() // Compile Error!!
}

----------------------------------------------

class Box<in T>

fun main(args: Array<String>) {
  val box1: Box<Int> = Box<Number>() // OK!!
  val box2: Box<Number> = Box<Int>() // Compile Error!!

}
  • out은 타입 파라미터를 공변성(convariant)으로 만듬 (out T는 공변성)
  • in은 타입 파라미터를 반공변성(contravariant)로 만듬 (in T는 반공변성)
  • variance 한정자(out / in)이 선언이 없이 제네릭을 사용하면 무공변성으로 정의가 되어, 만들어지는 타입들이 서로 연관성이 없음

함수 타입

val intToDouble: (Int) -> Number = { it.toDouble() }
val numberAsText: (Number) -> Any = { it.toShort() }
val identity: (Number) -> Number = { it }
val numberToInt: (Number) -> Int = { it.toInt() }
val numberHash: (Any) -> Number = { it.hashCode() }

printProcessedNumber(intToDouble)
printProcessedNumber(numberAsText)
printProcessedNumber(identity)
printProcessedNumber(numberToInt)
printProcessedNumber(numberHash)

//(int) -> Any 타입의 함수는 위와 같은 함수로도 동작
fun printProcessedNumber(transition: (Int) -> Any) {
  println(transition(42))
}
  • 함수 타입은 파라미터 유형과 리턴타입에 따라서 관계가 달라짐 
  • 코틀린 함수 타입은 모든 파라미터타입은 반공변성, 모든리턴타입은 공변성임
  • 함수 타입을 사용시 자동으로 variance 한정자가 사용됨

Variance 한정자 주의사항

  • 코틀린은 public in 한정자 위치에 공변성 타입 파라미터 (out 한정자)가 오는 것을 금지함 (in 한정자 위치에 있을 경우, 공변성과 업캐스팅을 연결해서, 원하는 타입을 아무거나 전달할 수 있기 때문)
  • 코틀린은 public out 한정자 위치에 반공변성 타입 파라미터가 오는 것을 금지함 (public out 위치는 암묵적으로 업캐스팅을 허용하나, 반공변성에 맞는 동작이 아님, 어떤타입이 들어오는지 확실하게 알 수가 없기 때문에)

Variance 한정자 위치

//선언부분에 사용
class Box<out T>(val value: T)
val box1: Box<String> = Box("Box")
val box2: Box<Any> = box1

//클래스와 인터페이스를 활용하는 부분에 사용
class Box<T>(val value: T)
val box1: Box<String> = Box("Box")
val box2: Box<out Any> = box1
  • 한정자위치는 크게 선언부분과 클래스와 인터페이스를 활용하는 부분 두위치에서 사용이 가능함
  • 선언 부분에서 사용하게 되면 클래스와 인터페이스 선언에 한정자가 적용이되며 모든곳에 영향을 끼침
  • 활용하는 위치에서 적용하면 특정한 변수에만 한정자가 적용이 됨
  • 활용하는 위치에 한정자를 적용할 경우 모든 인스턴스에 variance 한정자를 적용하면 안되고, 특정 인스턴스에만 적용해야 할 때, 위와같이 사용해야함