티스토리 뷰

Java

제네릭스(Generics) - 3. 와일드 카드 <?>

사용자 siyoon210 2018. 11. 4. 11:45
반응형

제네릭 클래스 선언 방법에 대해 알고 싶다면 이전 포스팅을 참고  ☞ 제네릭스(Generics) - 2. 제네릭 클래스 선언하기

와일드 카드 <?>

와일드카드란, 제네릭 클래스의 객체를 메소드의 매개변수로 받을 때, 그 객체의 타입 변수를 제한하는 것을 말한다. 역시나 정의로는 이해가 어렵다. 아래 설명된 예제를 보자.


참고로 와일드 카드를 생각할때는 제네릭 클래스 타입과 '관점'에 대한 분리가 되어야한다. '제한된 제네릭 클래스'는 제네릭 클래스를 선언하는 개발자의 관점에서 객체 생성시 사용 될 객체를 제한하는 것이고, 와일드카드는 메소드를 만드는 개발자의 관점에서 메소드에 사용 될 매개변수가 제네릭 클래스를 구현한 객체일때, 그 제네릭 클래스 '타입 변수'를 제한 하는 것이다. (여기서 말하는 메소드는 '제네릭 메소드'가 아닌, 일반적인 모든 메소드를 말한다.)

 

와일드 카드 <?> 는 언제 사용될까?

하나의 메소드를 만드는 과정중에 제네릭 클래스의 객체를 매개변수로 받는 경우가 있다고 하자. (이번에도 예제로는 ArrayList로 해보겠습니다.)

public void myMethod(ArrayList list) { . . . }

이 메소드는 ArrayList를 매개변수로 받고 있지만, ArrayList가 담고 있는 '타입 변수'는 Number를 상속받은 객체들이어야 정상적으로 작동 하도록 설계되었다. 하지만 ArrayList가 어떤 타입 변수를 가지고 있던지 간에 모두 받아 들일 가능성이 있다. 


이런 경우에 와일드카드를 이용하여서 ArrayList의 '타입 변수'를 지정 할 수있다.

public void myMethod(ArrayList<? extends Number> list) { . . . }

"메소드가 받아들일 매개변수 ArrayList의 타입 변수는 Number클래스와 Number클래스를 상속받은 클래스들의 객체들만 받는다!"라고 명시한 것이다.


와일드 카드<?>의 제한 종류

  • <? extends T> 와일드 카드의 상한 제한(upper bound) - T와 그 자손들을 구현한 객체들만 매개변수로 가능
  • <? super T> 와일드 카드의 하한 제한(lower bound) -T와 그 조상들을 구현한 객체들만 매개변수로 가능
  • <?> 제한 없음




제네릭 메소드에 대해서는 다음 포스팅 참고  ☞ 제네릭스(Generics) - 4. 제네릭 메소드

 

반응형
댓글
  • 프로필사진 나그네 궁금했던게 해결되었어요! 2020.06.29 17:45
  • 프로필사진 사용자 siyoon210 부족한 글인데 댓글주셔서 감사합니다! 2020.07.01 10:17 신고
  • 프로필사진 코린이 제네릭 메소드에서 와일드카드가 아닌 타입 파라미터를 쓴
    public <T extends String> void method(List<T> list) {} 과
    와일드 카드를 쓴
    public void method(List<? extends String> list) {} 의 차이점은 무엇인지 궁금합니다 ㅠㅠ
    2020.09.15 16:28
  • 프로필사진 사용자 siyoon210 '제네릭 메서드 타입과 와일드 카드의 기능적인 차이점은 없다'라고 이해해도 무방합니다.

    하지만 사실 좀 다릅니다 ㅎㅎ

    타입을 여러번 명시해야 할 경우 와일드카드 보다 제네렉 메서드 타입이 보다 간결하게 선언이 가능합니다.

    예를들어 와일드 카드로는 이렇게 긴 메서드 시그니처가
    List<? extends Number> wildCardTest(List<? extends Number> list, List<? extends Number> list2) {
    ....
    }

    <T extends Number> List<T> genericMethodTest(List<T> list, List<T> list2) {
    ....
    }
    제네릭 메서드 타입은 이렇게 간결하게 표현이 가능합니다.

    또 한가지 차이점이 있는데요.
    제네릭 메서드 타입으로 선언하게 되면 메서드 내부에서 가져온 타입은 사용이 가능합니다.
    하지만 와일드카드는 불가능합니다.

    예를들어
    List<? extends Number> wildCardTest(List<? extends Number> list, List<? extends Number> list2) {
    list.add(list.get(0));
    //컴파일 에러 발생!
    }
    와일드 카드는 list.get() 해온 경우라도 그걸 특정 타입으로 사용이 불가능합니다.

    <T extends Number> List<T> genericMethodTest(List<T> list, List<T> list2) {
    list.add(list.get(0));
    }
    반면에 제네릭 클래스 타입은 이런식으로 사용이 가능해요.

    마지막으로 한가지 차이가 또 있습니다.
    제네릭 타입은 하나의 타입 T로 선언된다면 반환값에 명시된 T도 파라미터의 들어간 T와 같은 타입임을 보장할 수 있습니다.

    하지만 와일드카드는 보장되지 않습니다.

    ----

    근데 이부분이 이해가 되면 그렇구나 이해를 하시고 안되시면 안되시는대로 넘어가셔도 무방하다고 생각해요 ㅋㅋㅋ
    2020.09.15 19:02 신고
  • 프로필사진 Bepoz 안녕하세요 덕분에 이해가 쏙쏙 됐습니다. 위 댓글에서 궁금한 점이 있어서 댓글 작성해봅니다! 와일드 카드에서 메서드 내부에서 가져온 타입은 사용이 가능하다는 것을 잘 이해하지 못했습니다. list.get(0); 한 값을 print 찍는 코드에는 컴파일 에러가 발생하지 않았습니다. 반면, 해당 파라미터로 받은 list 에 값을 집어넣으려고 하자 컴파일 에러가 나왔습니다. 답변에서 말씀하신 내부에서 가져온 타입은 사용이 가능하다라는 뜻은 단순히 값을 가지고 오는 것은 괜찮지만 추가하는 것은 불가능하다 라는 뜻일까요 ??? 그리고 혹시 해당 포스팅 글을 정리해서 올리고 싶은데 Reference에 링크를 추가하고 게시해도 되는지도 여쭤보고 싶습니다. 2021.02.25 17:52 신고
  • 프로필사진 사용자 siyoon210 네 말씀하신것처럼 get()해온 순간에는 컴파일 에러가 발생하지 않고, 집어 넣는 순간에 컴파일 에러가 발생하는게 맞습니다.

    그리고 참고하여서 포스팅 작성하셔도 좋습니다! 뭐 대단한 내용도 없는데요 뭘 ㅎㅎ 댓글 감사합니다.
    2021.02.28 20:26 신고
댓글쓰기 폼