자바에서는 파라미터화한 타입이 파라미터 타입에 대해 무공변성이다. 즉, A가 B의 부모 타입이라 하더라도 List<A>와 List<B> 사이에는 아무런 부모 자식 타입 관계가 성립하지 않는다. 따라서 List<A>와 List<B>는 컴파일 시점에 전혀 다른 두 가지 타입이다(그리고 런타임에는 두 타입이 같다). 무공변성 타입의 문제는 다음과 같은 코드를 작성할 수 없다는 데 있다.
fun <T> addAll(list1: MutableList<T>, list2: MutableList<T>) { for (elem in list2) list1.add(elem) } val ls = mutableListOf("A String") val la: MutableList<Any> = mutableListOf() addAll(la, ls) // <-- 컴파일되지 않음
String 타입의 elem이 List<Any>에 추가될 수 있으며, 그렇게 해도 아무 문제가 없다. 코틀린에서는 MutableList<Any>와 MutableList<String>을 동시에 MutableList<T>라는 제네릭 타입에 일치시킬 수 없다. 이 제네릭 함수가 제대로 작동하게 하려면 MutableList<Any>가 MutableList<String>의 상위 타입처럼 쓰일 수 있음을 컴파일러에 알려줘야 한다. 여기서 MutableList<Any>가 MutableList<String>의 상위 타입으로 쓰일 수 있는 이유는 la에서 값을 가져오기만 하고(out), 값을 넣는 일은 결코 없기 때문이다(in). 다음 코드와 같이 out이라는 한정자(qualifier)를 써서 이를 표현한다.