공변, 반공변이란 무엇일까
변성(Variance)
자바 제네릭은 기본적으로 불변
String
은Object
의 서브타입이다.- 하지만,
List<String>
은List<Object>
의 서브타입이 아니다. 즉 자바의 제네릭 타입은 불변(invariant) 이다. 변성(variance)이 없다.
불변임으로 인해서 나타나는 아래 문제점을 보자.
뭔가 이상한 addAll
코드
class Collection<E> {
void addAll(Collection<E> items) {
for (E e: src) {
push(e);
}
}
}
void copyAll(Collection<Object> to, Collection<String> from) {
to.addAll(from); // compile error
}
copyAll
메소드는 컴파일에러가 발생한다. 사실 copyAll
메소드는 아래와 같이 있어도 아무 문제가 없다. String 이 Object 로 들어갈 수 있기 때문이다. 하지만 자바의 제네릭은 불변이고 컴파일러가 변성을 판단할 수 없어 타입이 안전한지 모르므로 컴파일 오류를 낸다.
아래와 같아야 컴파일에러가 나지 않음
class Collection<E> {
void addAll(Collection<? extends E> src) {
for (E e: src) {
push(e);
}
}
}
이 경우는 extends
키워드로 src 파라미터를 공변으로 만들어주었다. 즉, 인풋으로 들어오는 Collection<String>
가 Collection<Object>
의 하위타입임을 컴파일러가 알게하면서 타입 안정성을 제공했다. 이를 공변(Covariance)이라 한다.
위 콜렉션에, popAll
메소드가 있어, 인풋파라미터에 내가 가진 값을 값 옮겨주는 메소드가 있다고 해보자. (이런 메소드를 많이 만들진 않겠지만)
class Collection<E> {
void popAll(Collection<? super E> dst) {
while(!isEmpty()) {
dst.add(pop());
}
}
}
이 경우는 super
키워드로 dst 파라미터를 반공변으로 만들어 주었다. 즉, 인풋으로 들어오는 Collection<Object>
가 Collection<String>
의 상위타입임을 컴파일러가 알게하면서 타입 안정성을 제공했다. 이를 반공변(Contravariance)이라 한다.
다소 헷갈리는 위 개념을 쉽게 기억하기 위해 Joshua Bloch 는 Effective Java 에서 PECS 라는 암기법을 제안했다.
PECS (Producer-Extends, Consuer-Super)
- 인풋 파라미터를 읽기만 하는 메소드를 가진 객체는
Producer
라 명명하고, 인풋 파라미터에extends
를 사용해서 공변을 만든다. - 인풋 파라미터를 쓰기만 하는 메소드를 가진 객체는
Consumer
라 명명하고, 인풋 파라미터에super
를 사용해서 반공변을 만든다.
자바의 제네릭에 타입 안정성을 제공하고 싶다면, PECS 를 기억하자.
'Java, Kotlin, Spring > Java' 카테고리의 다른 글
자바 애플리케이션 모니터링, javaagent, bytecode instrumentation (0) | 2023.03.20 |
---|---|
자바 힙메모리 확인, heap dump (0) | 2023.03.19 |
Garbage Collection (0) | 2023.03.19 |
[Java] 패키지, import, 접근제어자 (64) | 2021.01.06 |
java - 상속, super, 오버라이딩, 추상클래스, final, Object 클래스 (128) | 2020.12.23 |
댓글