이 포스팅은 Sun Microsystems의 Brian Goetz가 작성하고 오국환님이 번역하신 글(여기)의 내용을 Scavenger가 조금 더 읽기 편하게 생각하고 정리한 글입니다. 내용상에 문제가 있다면 지적해주세요.
Generic이란 사전에 확실히 알 수 없는 타입에 대한 클래스나 메소드를 정의하기 위해
고안되었고 Generic, 그 자체의 문법을 이해하는게 어려운 것은 아닙니다.
그러나 심층적인 개념으로 들어가면 상당히 난해한 경우가 발생하는게(심지어 전문가들 조차도) 사실입니다.
원론적인 불평을 하자면 초기의 Java 구조에 계속해서 다른 기능들이 덧붙여지기 때문에 처음의 간결하고 명시적인 Java를 고수하려는 마음과 충돌을 일으키는게 아닐까 합니다.
어쨋든 다음의 내용들이 조금 더 Generic을 사용하는데 도움을 드리고자 합니다.
와일드카드
와일드카드 ? 해당 클래스에 치환된 제네릭 변수에 종속되지 않는다는 의미를 지닙니다.
따라서 결과적으로 Object 형이 됩니다. 다음의 코드를 봅니다.
class Box<T> {
private T a;
public T get(){
return a; // a를 리턴
}
public void put(T element){
a = element;; // a에 element를 대입.
}
public void unbox(Box<?> box) { // Box<?> 타입의 box를 받는다.
// Box<?> 타입의 box에 있는 T를 get() 으로 리턴한다. 이 T는 Object형이 된다.
System.out.println(box.get());
Object obj = box.get(); // Object 만이 get() 메소드의 반환값을 가질 수 있다.
}
}
unbox메소드의 인자인 box객체의 메소드 get()를 호출했을 때, 분명히 내부적으로는
T타입으로 정의되어 있지만 Object형이 리턴됩니다. 이것은 다음의 사진으로 확실히
확인이 가능합니다. 이클립스가 적절하게 도와주는군요.
그런데 재미있는 것은, unbox메소드 내부에서 box.put() 메소드를 사용하는 것은 불가능
하다는 것입니다. 어떠한 코드에 대해 프로그래머가 이해하는 사항과 컴파일러가 이해하는
사항이 일치하지 않는다는 점입니다. 따라서, 다음의 코드 중 마지막 라인을 제외한 모든
라인은 Error입니다.
// main
public class generic_exam {
public static void main(String args[]) {
Box<Integer> b = new Box<Integer>(); // Integer타입으로 b 객체 생성.
Box<Double> b2 = new Box<Double>(); // Double타입.
b2.put(3.1); // Success.
// b객체의 unbox메소드 인자로 b2를 넘김.
// unbox 메소드의 Box타입으로 T가 아닌 ?이기 때문에 Success.
// ? 에 대해 감이 안잡힌다면 한번 Box<T> box 로 바꿔본다.
b.unbox(b2);
}
}
// unbox 메소드 내부.
public void unbox(Box<?> box) {
System.out.println(box.get());
box.put(new Double(3)); // Double 은 될 것 같지만, Error
box.put(new Object()); // Object 마저도 Error
T imsi_t; // T타입의 imsi_t 변수 선언.
box.put(imsi_t); // Error
imsi_t = null; // imsi_t 에 null값을 지시.
box.put(imsi_t); // Error
box.put(null); // Success. 이 라인을 제외한 모든 타입이 인자로써 불가.
}
이 에러의 이유는 윗 링크에 자세히 설명되어 있는데요,
"capture#337 of ?"란 도대체 무슨 뜻인가? 컴파일러는 rebox()의 box 매개변수와 같이 타입에 와일드카드를 가진 변수를 만나면, box가 Box<T>인 T가 어딘가 있을 것을 안다. T가 무슨 타입인지는 모르나 (중략) 이는 알려지지 않은 타입 사이에 서로 연관성이 없기 때문이다.
이 에러 메시지가 말하고자 하는 것은 put()의 실제 매개변수 타입이 형식상 매개변수 타입과 호환되는지 검증할 수 없다는 점이다.(중략) 이제 컴파일러는 이 Object를 "capture#337 of ?" 지시자가 가리키는 타입에 사용할 수 있는지 정적으로 검증할 수 없다."
쉽게 풀이하자면, 컴파일러가 put메소드의 인자로 어떤 타입을 넣을 때, 그 타입이 put 메소드의 T 타입과 호환성이 있는지에 대해 검증할 수 없다는 점입니다.
뉘앙스를 주의할 필요가 있죠, 검증할 수 없다는거지 컴파일러가 '틀렸어' 라고 말하는게 아닙니다. 프로그래머는 알고 있어도 컴파일러는 알 수 없다는 의미이지요. 그래서 정상적으로 처리 될 코드가 에러를 뱉어내는 상황이 발생하는 겁니다.
이것을 이해하는게 쉬운 일이 아닙니다. 왜냐면, 와일드카드 ?의 개념은 제네릭 요소로 사용되는 T, E 등의 의미와는 정 반대의 의미를 지니기 때문입니다. T, E등의 제네릭 요소는 받은 객체를 기준으로 삼지만 와일드카드 ?는 받는 객체가 아닌 제네릭 요소에 자체를 기준으로 삼고 있기 때문입니다.
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST





