-
와일드카드 ? vs 정규 타입 매개변수 TJava/Basics 2020. 7. 22. 21:49
와일드카드 ? 와 정규 타입 매개변수 T의 차이를 살펴보자.
■Box 예제
먼저 Box라는 인터페이스와 Box를 rebox하는 메서드가 있다.
public interface Box<T> {
T get();
void put(T element);
}
public void rebox(Box<?> box) {
box.put(box.get());
}rebox 메서드를 호출해본다고 생각해보자.
위 메소드의 호출부가 컴파일될 때, 매개변수의 와일드카드 타입이 결정된다.
그리고 box 내의 객체를 get 한 후 다시 push 한다.
별다른 오류가 없을 것 같지만 사실 다음과 같은 에러가 뜬다.
Rebox.java:8: put(capture#337 of ?) in Box<capture#337 of ?> cannot be applied
to (java.lang.Object)
box.put(box.get());
^
capture 에러가 뭘까?
■capture란?
capture#337 of ?
이 오류가 무엇을 의미할까?
컴파일러가 (rebox 메서드의 box 파라미터와 같은) 와일드카드 매개 변수를
만나면 Box 형태에 맞출 수 있는 특정한 T를 생성한다.
아래는 영어로된 원문이다.
When the compiler encounters a variable with a wildcard in its type,
such as the box parameter of rebox(),
it knows that there must have been some T for which box is a Box<T>.
It does not know what type T represents, but it can
create a placeholder for that type to refer to the type that T must be.
컴파일러가 와일드카드를 이용해 생성하는 그 특정 T들을 capture라고 한다.
위 메서드의 경우에, 컴파일러는 "capture#337 of ?" 라는 capture를 생성한 것이다.
capture는 컴파일러가 임의로 생성한 타입이고, 개발자는 이를 예측할 수 없다.
따라서 capture 형태의 매개변수화 타입이 정해지면
get은 할 수 있지만 put은 할 수 없다.
모든 객체들은 Object 타입으로 받을 수 있지만
해당 클래스 capture 타입을 미리 정의할 순 없다.
(해당 타입을 알아야 put을 실행시킬 수 있다)
컴파일러는 만나는 모든 ? 마다 독립적인 capture를 생성한다.
예를들어 (Pair<?,?> x, Pair<?,?> y) 라는 매개 변수가 있다면
4개의 독립적인 capture가 생성된다.
각 ? 에는 연관성이 없기 때문. (연관성이 있으면 같은 capture가 지정되나보다)
■에러의 진짜 의미
위 에러 메세지는
"아래 두 타입이 비교 가능한지 확인할 수 없기 때문에 put을 호출할 수 없다"
는 것을 의미한다.
두 타입은 아래와 같다.
- put에 전달될 실제 파라미터 타입 (get()의 return 타입인 Object)
- formal parameter 타입 (?가 변환되는 capture#337 of ?)
? 는 애초에 ? extends Object를 의미하므로
컴파일러는 get 메소드의 반환 타입을 Object으로 인식한다.
이 경우에 더 정확히는 ? extends Object보단
capture#337 of ? extends Object인 것 같다.
결국엔 입력되는 타입과 받는 타입이 호환되지 않아 실행이 불가능한 것이다.
■정규 타입 매개변수와의 비교
private void reboxWildcard(Box<?> box) {
Object o = box.get();//무조건 Object 타입으로만 참조 가능
box.put(o);//capture 형태로 형변환 할 방법이 없기 때문에 put 불가능 -> 컴파일 에러남!!!!!!!!!!!!!
}
private <T> void reboxT(Box<T> box) {
Object o = box.get();//Object 타입 참조 가능
box.put((T) o);
T o2 = box.get();//매개변수 타입으로도 참조 가능
box.put(o2);
}■결론
결국 와일드카드로 매개 변수를 선언할 경우, get은 얼마든지 할 수 있다.
Object로 받아서 쓸 수 있기 때문이다.
하지만 set, put 등은 할 수 없다. capture#337 of ? 타입과 같은 형태로
형 변환이 돼야 저장할 수 있는데, 저 형태는 실제 호출부가 컴파일되는 시점에
정해지기 때문에 코드로 어찌 넣을 방법이 없기 때문이다.
반면 정규 타입 매개변수를 사용하면 get은 물론이고 set, put도 할 수 있다.
코드를 작성할 때, T와 같은 저장할 타입을 명확히 알 수 있기 때문이다.
메소드를 사용하는 입장에서 바라본다면
와일드카드를 사용하는 메소드는 내부에서 null 이외의 객체를 저장할 수 없기 때문에
조금 더 안심(?)하고 사용할 수 있을 듯 하다.
그래서 와일드카드를 사용하는 메소드는 public api로 적합하다고 한다.
출처 : https://www.ibm.com/developerworks/library/j-jtp04298/index.html
Contents > Wildcard capture
'Java > Basics' 카테고리의 다른 글
stackoverflow 번역하기 :: static 메소드를 mocking 할 수 없는 이유 (0) 2021.05.19 유용한 JVM 명령어 모음 (0) 2020.10.01 Java Thread(with Multi Thread) (0) 2020.06.21 같은 value 값을 가지는 String들의 hashcode가 같은 이유 (0) 2020.06.19 메모리 주소만으로 Heap의 인스턴스에 접근할 수 있을까? (2) 2020.06.17 댓글
- put에 전달될 실제 파라미터 타입 (get()의 return 타입인 Object)