Agile Java Lesson 14

기본

일반화

Generic은 간단히 사용할 땐 아무런 문제가 없는데 클래스나 메소드를 설계 하려고 하면 이해할 수 없는 에러가 발생해서 막연히 손대기 어렵다고 생각했었습니다. 그런데 그 원인이 내부 동작을 이해하지 못해서 였네요.

1. erasure

Map<Date, String> map = new HashMap<Date, String>();

이런 객체를 만들었을 때 K, V로 선언한 곳이 Date나 String으로 대체되는 줄 알고 있었는데 내부에선 이러한 parameterized type을 전부 날려버리고 Object 타입으로 받아서 캐스팅을 합니다. 따라서 몇 가지 제한이 생기는데 Sun은 하위 버전 호환성을 위해서 이런 구조를 채택했다고 하네요.

2. extends

extends는 parameterized type의 최상위 타입을 Object가 아닌 extends의 타입으로 제한합니다. 컴파일러는 parameterized type을 최상위 타입으로 바꾸기 때문에 하위 타입을 사용할 수도 있습니다.

public class EventMap<K extends java.util.Date, V> extends MultiHashMap<K, V> { ... }

EventMap<java.util.Date, String> map = new EventMap<java.util.Date, String>();
 map.put(new java.util.Date(), "Date Type");
 map.put(new java.sql.Date(new java.util.Date().getTime()), "SQL Date Type");

EventMap 내부에선 K를 java.util.Date 타입이라고 가정할 수 있습니다.

private boolean hasPassed(K date) {
 Calendar when = new GregorianCalendar();
 when.setTime(date);
...

3. wildcard

Generic은 기본적으로 해당 타입으로만 제한하는데 어떤 타입이 올지 알 수 없는 경우 wildcard를 사용할 수 있습니다.

public static String concatenate(List<?> list, Object object) { ... }

문제는 wildcard를 사용할 경우 어떤 타입이 올지 알 수 없기 때문에 parameterized type을 사용한 메소드를 호출할 수 없다는 겁니다.

list.add(object); //! Error

에러가 발생하는 이유는 메소드의 파라미터로 넘겨받는 실제 list는 Date로 parameterized 됐는데 해당 메소드에서는 그걸 알 방법이 없기 때문입니다. 결국 다른 타입의 객체가 들어가서 타입 안정성을 해칠 수도 있습니다. 만약 메소드에서 List<? extends Object> list 같이 코드를 수정한다고 해도 이미 파라미터로 넘어오는 list 객체는 어떤 타입으로 erasure 됐기 때문에 에러가 발생합니다. (extends하는 타입으로 parameterized type을 바꿔야 하는데 이미 erasure 돼버렸죠.)

반면에 list.get() 메소드는 이미 erasure된 후, 캐스팅된 객체를 꺼내오기 때문에 이러한 문제가 없습니다. 따라서 wildcard는 데이터 구조에서 엘리먼트를 읽어들일 때 사용해야 합니다.

4. Generic Method

위의 문제는 제네릭 메소드를 사용해 해결할 수 있습니다.

public static <T> String concatenate(List<T> list, T object) { ... }

이 경우 T가 파라미터로 넘어오는 list 객체의 parameterized type으로 바뀌기 때문에 add() 메소드를 호출해도 문제가 없습니다.

또는 와일드카드를 사용한 메소드에서 제네릭 메소드를 호출하면 제네릭 메소드에서는 와일드카드의 타입을 알아낼 수 있기 때문에(wildcard capture) 이런 문제를 해결할 수 있습니다.

정리하자면 메소드의 파라미터 간 또는 파라미터와 리턴 타입에 의존성이 있는 경우 제네릭 메소드를 사용해야 하고 그렇지 않을 경우 와일드카드를 사용합니다.

6. super

extends와는 반대로 하위 타입을 제한합니다. 따라서 super의 타입의 수퍼 클래스 타입도 사용할 수 있습니다.

Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중