ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 와일드카드 ? vs 정규 타입 매개변수 T
    Java/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을 호출할 수 없다"


    는 것을 의미한다.


    두 타입은 아래와 같다.


    1. put에 전달될 실제 파라미터 타입 (get()의 return 타입인 Object)

    2. 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

    댓글

Designed by Tistory.