Java의 제네릭은 기본적으로 무공변의 성질을 가지고 있어서 전달받은 딱 그 타입으로만 서로 캐스팅이 가능하다.
하지만 이는 코드를 유연하지 않게 만들기 때문에, 와일드 카드 타입을 이용해서 변성을 이용할 수 있게 했다.
제네릭의 공변은 상한 경계 와일드카드를 이용하여 클래스를 상속한 자손들을 한정으로 업캐스팅이 가능하다는 성질이고,
(parent = child; : Integer 가 Object의 하위 타입일 경우, C<Integer>는 C<? extends Object>의 하위 타입이 되는 것)
제네릭의 반공변은 하한 경계 와일드카드를 이용하여 특정 클래스의 조상 클래스들을 한정으로 다운캐스팅이 가능하다는 성질이다.
(child = parent; : Object가 Integer의 상위 타입일 경우, C<Object>는 C<? super Integer>의 하위 타입이 되는 것)
변성(Variance) 이란? / 제네릭의 무공변
반공변, 공변, 무공변은 변성이라는 개념안에서 나온 단어들이다.
JAVA뿐만 아니라 이 변성은 다른 프로그래밍 언어를 설명할 때도 나오는 개념으로
타입의 계층 관계에서 서로 다른 타입 간에 어떤 관계가 있는지 나타내는 지표이다.
•
공변 : S 가 T 의 하위 타입이면,
◦
S[] 는 T[] 의 하위 타입이다.
◦
List<S> 는 List<T> 의 하위 타입이다.
•
반공변 : S 가 T의 하위 타입이면,
◦
T[] 는 S[] 의 하위 타입이다. (공변의 반대)
◦
List<T> 는 List<S> 의 하위 타입이다. (공변의 반대)
•
무공변 / 불공변 : S 와 T 는 서로 관계가 없다.
◦
List<S> 와 List<T> 는 서로 다른 타입이다.
// 공변성
Object[] Covariance = new Integer[10];
// 반공변성
Integer[] Contravariance = (Integer[]) Covariance;
// 공변성
ArrayList<Object> Covariance = new ArrayList<Integer>();
// 반공변성
ArrayList<Integer> Contravariance = new ArrayList<Object>();
Java
복사
제네릭은 기본적으로 무공변이다. 따라서 제네릭은 전달받은 딱 그 타입으로만 서로 캐스팅이 가능하다.
public static void main(String[] args) {
MyList<Number> list;
collection<Integer> intList = Array.asList(1,2,3,4,5);
list = new MyList<>(intList); // 컴파일 에러 발생.
// 🔴 제네릭은 무공변이기 때문에, <Number>로 선언하였으면 Number 객체만 들어가야 한다.
// Inteager 가 자바언어에서 Number의 하위 객체여도 말이다.
Java
복사
하지만 이는 자바의 다형성에 어울리지 않는 듯 보인다.
그래서 제네릭은 한정적 와일드카드 타입 기능을 통해 공변을 제공하기도 한다.
와일드 카드 타입을 활용한 제네릭
와일드카드 | 네이밍 | 설명 | 변성 |
<?> | Unbounded wildcards
비한정적 와일드 카드 | 제한 없음 (모든 타입이 가능) | |
<? extends U> | Upper Bounded Wildcards
상한 경계 와일드카드 | 상위 클래스 제한
(U와 그 자손들만 가능) | 공변성 적용 |
<? super U> | Lower Bounded Wildcards
하한 경계 와일드카드 | 하위 클래스 제한
(U와 그 조상들만 가능) | 반공변성 적용 |
•
상한 경계 와일드카드
ArrayList<? extends Object> parent = new ArrayList<>();
ArrayList<? extends Integer> child = new ArrayList<>();
parent = child; // 공변성 (제네릭 타입 업캐스팅)
Java
복사
→ Integer 가 Object의 하위 타입일 경우, C<Integer>는 C<? extends Object>의 하위 타입이 되는 것.
•
하한 경계 와일드카드
ArrayList<? super Object> parent = new ArrayList<>();
ArrayList<? super Integer> child = new ArrayList<>();
child = parent; // 반공변성 (제네릭 다운캐스팅)
Java
복사
→ Object가 Integer의 상위 타입일 경우, C<Object>는 C<? super Integer>의 하위 타입이 되는 것.
와일드카드 타입을 통해 타입 매개변수 지점에 변성을 정하는 자바의 방식을 사용지점 변성(use-site variance) 이라 한다.
자바와 같이 JVM을 이용하는 코틀린에서도 사용자 지점 변성을 제공한다.
참조