728x90
728x90
가변인수(varargs) 메서드와 제네릭은 자바 5때 함께 추가되었지만 서로 잘 어울러지지 못한다. 가변인수는 메서드에 넘기는 인수의 개수를 클라이언트가 조절할 수 있게 해주는데, 이 구현방식에 허점이 있다. 가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어진다. 그런데 이 방식은 내부로 감춰야 했을 이 배열이 클라이언트에 노출되는 것이다. 그 결과 가변인수 매개변수에 제네릭이나 매개변수화 타입이 포함되면 알기 어려운 컴파일 경고가 발생한다. 우리는 이전 아이템들을 학습하면서 실체화 불가 타입은 런타임에는 컴파일타임보다 타입 관련 정보를 적게 담고 있음을 배웠다. 그리고 거의 모든 제네릭과 매개변수화 타입은 실체화되지 않는다. 메서드를 선언할 때 실체화 불가 타입으로 가변인수 ..
아이템 28에서 이야기했듯 매개변수화 타입은 불공변(invariant)이다. 예를들어 List은 List가 하는 일을 제대로 수행하지 못하니 하위 타입이 될 수없다(리스코프 치환 원칙에 어긋난다). 하지만 때론 불공변 방식보다 유연한 무언가가 필요하다. 아이템29에서 언급된 Stack 클래스를 떠올려보자. 아래에 Stack의 public API를 나열했다. publci class Stack public Stack(); public void push(E e); public E pop(); public boolean isEmpty(); } 여기에 일련의 원소를 스택에 넣는 메서드를 추가해야 한다고 해보자. public void pushAll(Iterable src) { for (E e : src) push(..
클래스와 마찬가지로, 메서드도 제네릭으로 만들 수 있다. 매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다. Collections의 알고리즘 메서드는 모두 제네릭이다. 제네릭 메서드 작성법은 제네릭 타입 작성법과 비슷하다. 다음은 두 집합의 합집합을 반환하는, 문제가 있는 메서드다. public static Set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll(s2); return result; } 컴파일은 되지만 경고가 두 개 발생한다. 경고를 없애려면 이 메서드를 타입 안전하게 만들어야 한다. 메서드 선언에서의 세 집합(입력 2개, 반환 1개)의 원소 타입을 타입 매개변수로 명시하고, 메서드 안에서도 이 타입 매개..
JDK가 제공하는 제네릭 타입과 메서드를 사용하는 일은 일반적으로 쉬운 편이지만, 제네릭 타입을 새로 만드는 일은 조금 더 어렵다. 아이템 7에서 다룬 단순한 스택 코드를 다시 살펴보자. public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (si..
배열게 제니릭 타입에는 두 가지 차이가 있다. 1. 배열은 공변(covariant)이다. 어려워 보이는 단어지만 뜻은 간단하다. Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위타입이 된다(공변, 즉 함께 변한다는 뜻이다). 반면, 제네릭은 불공변(invariant)이다. 즉, 서로 다른 타입 Type1과 Type2가 있을 때, List은 List의 하위 타입도 아니고 상위 타입도 아니다. 이것만 보면 제네릭에 문제가 있다고 생각할 수 있지만, 문제가 있는 쪽은 리스트가 아니라 오히려 배열 쪽이다. 다음은 문법상 허용되는 코드다. Object[] objectArray = new Long[1]; objectArray[0] = "타입이 달라 넣을 수 없다."; // Array..
제네릭을 사용하기 시작하면 수많은 컴파일러 경고를 보게 될 것이다. 비검사 형변환 경고, 메서드 호출 경고, 비검사 매개변수화 가변인수 타입 경고, 비검사 변환 경고 등이다. 제네릭에 익숙해질수록 마주치는 경고 수는 줄겠지만 새로 작성한 코드가 한번에 깨끗하게 컴파일되리라 기대하지는 말자. 대부분의 비검사 경고는 쉽게 제거할 수 있다. 코드를 다음처럼 잘못 작성했다고 해보자. Set exaltation = new HashSet(); 그러면 컴파일러는 무엇이 잘못됐는지 친절하게 알려줄 것이다. (javac 명령줄인수에 -Xlint:uncheck 옵션을 추가해야 한다). 컴파일러가 알려준대로 수정하면 경고가 사라진다. 사실 컴파일러가 알려준 타입 매개변수를 명시하지 않고, 자바 7부터는 지원하는 다이아몬드 ..