You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
아이템 31. 한정적 와일드카드를 사용해 API 유연성을 높이라
오해
서로 다른 타입 Type1과 Type2가 있을 때
List<Type1>
은List<Type2>
의 상위 타입도 하위 타입도 아니다.List<String>
은List<Object>
의 하위 타입이 아니라는 뜻List<Object>
에는 어떤 객체든 넣을 수 있지만List<String>
은 문자열만 넣을 수 있음List<String>
은List<Object>
가 하는일을 제대로 수행하지 못하니 하위 타입이 될 수 없음 (리스코프 치환 원칙에 어긋남 -> 아이템 10. equals는 일반 규약을 지켜 재정의하라 #26 참고)불공변 방식으로 인한 문제점
Stack의 public API를 추려보면
여기에 일련의 원소를 스택에 넣는 메서드를 추가해야 하는 상황이라 가정해보자
먼저 Stack 클래스를 상속하여 새로운 pushAll 메서드를 확장하기 위해 CustomStack을 구현
그럼
Stack<Number>
로 선언한 후 pushAll(intVal) 호출 시 무슨일이 발생하는지 알아보면 (intVal은 Integer 타입)잘못된 생각을 하면 Integer는 Number의 하위 타입이니 잘 동작할 것으로 예상하나
실제로는 위의 오류를 보이며 컴파일이 되지 않는다.
해결책 --> 불공변 방식보다 유연한 무언가...
자바에서는 위의 상황에 대처할 수 있는
한정적 와일드카드 타입
이라는 특별한 매개변수화 타입을 지원.한정적 와일드카드 타입
Iterable<E>
-->Iterable<? extends E>
E의 Iterable
이 아니라E의 하위타입의 Iterable
이 되어야 함.이제 위의 문제인 코드를 수정해보면
이제 위의 문제의 코드는 문제 없이 컴파일 된다.
해당 코드로 수정으로 인해 CustomStack은 물론 이를 사용하는 클라이언트 코드도 말끔히 컴파일 됨.
CustomStack과 클라이언크 모두 깔끔히 컴파일 --> 모든 것이 타입 안전하다!!
스터디원 분들 위 방식대로 popAll() 메서드 또한 구현해보길 바랍니다. !!
결론
유연성을 극대화하려면 원소의 생상자나 소비자용 입력 매개변수에 와일드카드 타입을 사용하라!!
와일드카드 타입이 굳이 필요없는 경우, 사용하지 말아야 하는 경우
입력 매개변수가 생산자와 소비자 역할을 동시에 하는 경우
와일드카드 타입을 사용해야 하는 경우
<? extends T>
를 사용하고 소비자라면<? super T>
를 사용하라위 원칙을 통해 이전 아이템에서 설명한 메서드와 생성자 선언을 다시 살펴보면 (#59 참고)
위 생성자로 넘겨지는 choices 컬렉션은 T 타입의 값을 생산하기만 하니 (그리고 나중을 위해 저장) T를 확장하는 와일드카드 타입을 사용해 선언하는 것이 좋음
위 처럼 수정하면 생기는 실질적 차이?
Chooser<Number>
의 생성자에List<Integer>
를 넘기고 싶다고 가정해보자s1과 s2 모두 E의 생산자 이므로 PECS 공식에 따라 수정해보면
마찬가지로 위처럼 수정하면 이전에 컴파일되지 않던 아래 코드도 말끔히 컴파일이 됨!
클래스 사용자가 와일드카드 타입을 신경 써야 한다면 해당 API에 문제가 있을 가능성이 크다
자바 8 이전의 컴파일 문제
해결책
위 코드를 좀 더 다듬으면
가 된다.
List<E>
를List<? extends E>
로 수정Comparable<E>
를 확장한다고 정의했는데 이 때Comparable<E>
는 E 인스턴스를 소비함 (그리고 선후 관계를 뜻하는 정수를 생산)Comparable<E>
를 한정적 와일드카드 타입인Comparable<? super E>
로 대체Comparable<E>
보다는Comparable<? super E>
를 사용하는 편이 나음Comparator<E>
보다는Comparator<? super E>
를 사용하는 편이 나음위 처럼 수정하면 생기는 실질적 차이?
위 코드의 리스트는 오직 수정된 max 로만 처리할 수 있다.
수정 전 max가 해당 리스트를 처리할 수 없는 이유
정리하면 Comparable 혹은 Comparator를 직접 구현하지 않고, 직접 구현한 다른 타입을 확장한 타입을 지원하기 위해 와일드카드가 필요
복잡할 수 있으니 위의 코드를 정리해보면
타입 매개변수와 와일드카드 둘 중 어느 것을 사용해도 되는 경우
타입 매개변수와 와일드카드에는 공통되는 부분이 있어 메서드 정의시 둘 중 아무거나 사용해도 괜찮을 때가 많다
주어진 리스트에서 명시한 두 인덱스의 아이템들을 교환하는 정적 메서드를 구현한 두가지 방식
Q) 위 방식 중 어떤 선언이 나을까?
기본 규칙
위 코드르 컴파일시 별로 도움이 되지 않는 오류 메시지가 나옴

방금 꺼낸 원소를 리스트에 다시 넣을 수 없는 충격적인 상황이 발생한다. 이유는 ???
해결책
다행히도 런타임 오류를 낼 가능성이 있는 해결 방법인 형변환이나 로 타입을 사용하지 않고도 가능하다
따라서 최종적으로 깔끔히 컴파일됨을 확인할 수 있을것이다.
결과
정리
Beta Was this translation helpful? Give feedback.
All reactions