형 변환에 대해 파고들면 Covariant, Contravariant, Invariant과 같은 용어들을 쉽게 만날 수 있다.
세 용어 모두 형 변환에 있어 슈퍼타입-서브타입 관계성을 정의한다.
A
와 B
를 타입, f
를 형 변환, 그리고 <=
을 서브타입 관계성이라 가정하자. (즉, A<=B
는 A
가 B
의 서브타입을 의미한다)
A<=B
일 때 f(A) <= f(B)
면 CovariantA<=B
일 때 f(B) <= f(A)
면 Contravariantf(A) = List<A>
로 다시 확인해보자
List<String>
이 List<Object>
의 서브타입임을 의미한다.List<Object>
이 List<String>
의 서브타입임을 의미한다.List<String>
와 List<Object>
는 서로 변환되지 않는, String
과 Object
간 서브 타입 관계성이 사라짐을 의미한다. Java에서f(A) = A[]
는 Covariant하다. 즉, String[]
은 Object[]
의 서브타입에 해당한다.
Java에서는 List<String>
과 List<Object>
는 Invariant하다. Generics 자체가 invariant로 인정되는 경우가 많다.
또 예를 들자면, Integer
는 Number
의 서브 타입이지만, List<Integer>
는 List<Number>
의 서브 타입이 아니다. 이 둘의 공통 부모는 List<?>
뿐이다.
출처: 오라클 공식 문서
그래서 Invariant 관계에 놓인 이 두 리스트에 대해 관계성을 형성하기 위해서는 와일드카드를 사용해야 한다.
List<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList;
List<? extends Number> numList
는 Number
의 서브타입 (Integer
도 해당)의 리스트를 모두 허용하기 때문에 numList
에 intList
를 지정할 수 있다.
https://stackoverflow.com/questions/8481301/covariance-invariance-and-contravariance-explained-in-plain-english
https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html