이 글은 기존 운영했던 WordPress 블로그인 PyxisPub: Development Life (pyxispub.uzuki.live) 에서 가져온 글 입니다. 모든 글을 가져오지는 않으며, 작성 시점과 현재 시점에는 차이가 많이 존재합니다.
작성 시점: 2017-09-06
코틀린의 List 는 Immutable interface 를 가지고 있다.
1번 글에서 있었던 operate 메소드에 Array 대신 List을 작성해보자.
fun operate(person: List<Person>)
신기하게도, Person 과 Employee 를 둘 다 받을 수 있다. 왜냐면, List는 어떤 클래스를 상속받는가에 따라 변할 수 있는 성질을 가지고 있기 때문이다.
이를 Covariant(공변) 라 부른다.
반대로 Employee 가 어떤 클래스를 상속하는가에 따라 변할 수 있는 성질을 가지고 있다면, 이를 Contravariant(반변) 라 부른다.
List는 제너릭 부분에 out 키워드를 가지고 있는데, 이 out가 이번 글에서 중요한 개념을 가지고 있다.
public interface List<out E> : Collection<E> {
제너릭 인터페이스를 작성해보자.
interface ReadOnlyRepo<T> {
fun getId(id: Int): T
fun getAll(): List<T>
}
그러면 Type parameter can have out variance 라고 힌트를 보여주는데, 이 인터페이스는 T 를 단일 객체이든, 리스트든 전달만하기 때문이다.
interface WriteOnlyRepo<T> {
fun setId(id: T)
fun setAll(list: List<T>)
}
반대로, 어떤 제너릭 메소드가 T를 쓰기만 한다면, IDE는 Type parameter can have in variance 라고 힌트를 보여준다.
...조금 정리해보자.
클래스 C 와 T가 있다면, 클래스 C 가 T 파라미터에 따라 같이 변한다(공변)면, T 는 공변 파라미터다. 쉽게 생각해서 클래스 C가 T의 생산자가 될 수 있지만, 소비자가 되지 못한다.
반대로, 클래스 C가 T 파라미터에 따라 반대로 변한다(반변)면, T는 반변 파라미터다. 쉽게 생각해서 클래스 C가 T의 소비자가 될 수 있지만, 생성자가 되지 못한다.
자바로 표현하자면 공변은 ? extends T 이고, 반변은 ? super T 인 셈이다.