-
[Kotlin] Generic 제네릭이란?, variance : in, outKotlin 2023. 6. 8. 01:15반응형
제네릭이란?
<> 이렇게 생긴 코드를 많이 봤을 것이다. 이게 바로 제네릭이다.
제네릭을 사용하면 타입 파라미터 (type parameter)를 받는 타입을 정의할 수 있다.
예를 들어, List라는 타입이 있다면 그 안에 들어가는 원소의 타입을 정의한다.
val members: List<String> = listOf("A","B","C")
>> members 는 문자열을 담은 리스트다. ( 그냥 리스트가 아니라 )
Map 클래스는 key 타입과 value 타입을 타입 파라미터로 받으므로 Map<K,V>가 된다. 이런 제네릭 클래스에 Map<String, Person> 처럼 구체적인 타입을 타입 파라미터로 넘기면 타입을 인스턴스화할 수 있다.
Java와 다르게 Kotlin에서는 제네릭 타입의 타입 인자를 프로그래머가 명시하거나 컴파일러가 추론할 수 있어야 한다.
Variance : in, out
Invariant (불공변성)
// Java List<String> strs = new ArrayList<String>(); List<Object> objs = strs; // !!! A compile-time error here saves us from a runtime exception later. objs.add(1); // Put an Integer into a list of Strings String s = strs.get(0); // !!! ClassCastException: Cannot cast Integer to String
위의 코드의 경우 2번째 줄에서 컴파일 에러가 난다. 이유는 List<String>은 List<Object>의 sub type이 아니기 때문이다.
하지만 subtype이라고 하더라도 4번째 줄에서 런타임에러가 발생한다. String이 아닌 Object가 반환되기 때문이이다.
이펙티브 코틀린 item 24: 제네릭 타입과 variance 한정자를 활용하라
다음과 같은 제네릭 클래스가 있다고 해보자
class Cup<T>
타입 파라미터 T는 variance 한정자(out 또는 In) 이 없으므로 기본적으로 invariant 이다.
invariant 라는 것은 제네릭 타입으로 만들어지는 타입들이 서로 관련성이 없다는 것이다.
예를 들어 Cup<Int> 와 Cup<Number>, Cup<Any> 는 서로 어떠한 관련도 없다.
하지만 어떤 관련성을 원한다면, out 또는 in 이라는 variance 한정자를 붙여야한다.
Out : 타입 파라미터를 Convariant(공변성) 으로 만든다.
class Cup<out T> open class Dog class Puppy: Dog() fun main(args: Array<String>){ val b: Cup<Dog> = Cup<Puppy>() // OK val a: Cup<Puppy> = Cup<Dog>() // 오류 }
A가 B의 subtype일 때, Cup<A> 가 Cup<B>의 서브타입이라는 의미이다.
In : 타입 파라미터를 contravariant(반변성) 으로 만든다.
class Cup<in T> open class Dog class Puppy: Dog() fun main(args: Array<String>){ val b: Cup<Dog> = Cup<Puppy>() // 오류 val a: Cup<Puppy> = Cup<Dog>() // OK }
A가 B의 subtype일 때, Cup<A>가 Cup<B>의 슈퍼타입이라는 의미이다.
참고)
https://kotlinlang.org/docs/generics.html
반응형'Kotlin' 카테고리의 다른 글
[Kotlin] Scope Function : apply, run, with, also, let 정리 (0) 2023.05.28