자바 제네릭을 제대로 이해하기 위해서는 아래와 같은 용어에 대한 명확한 이해가 필요하다.
용어 |
예 |
Parameterized type |
List<String> |
Actual type parameter |
String |
Generic parameter |
List<E> |
Formal type parameter |
E |
Unbounded wildcard type |
List<?> |
Raw type |
List |
Bounded type parameter | <E extends Number> |
Recursive type bound |
<T extends Comparable<T>> |
Bounded wildcard type |
List<? extends Number> |
Generic method |
static <E> List<E> asList(E() a) |
Type token | String.class |
자바 1.5이전에는 아래와 같이 사용하곤 했다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Main { public static void main(String[] args){ Collection studentNames = new ArrayList<>(); studentNames.add("wonyoung"); studentNames.add("somin"); studentNames.add(1); for (Iterator iterator = studentNames.iterator(); iterator.hasNext();) { String name = (String) iterator.next(); System.out.println(name); } } } | cs |
위와 같이 쓰면 아래와 같이 ClassCastException예외가 발생하게 된다.
1 2 3 4 5 6 7 8 9 10 11 12 | wonyoung somin Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at com.jp.Main.main(Main.java:10) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Process finished with exit code 1 | cs |
항상 강조하지만 오류는 가장 빨리 발견해야 하며, 컴파일 할때 발견할 수 있으면 가장 이상적이다. 그러나 위 예제는 컴파일때는 전혀 이상한 점을 못찾다가 실행 중에 발견되게 된다.
컴파일러는 위 Collection 자료형이 "String객체만 보관한다" 라는 명시적인 문구가 없기때문에 문제가 발생하게 되는 것이다. 이것을 해결할 방법이 바로 아래와 같이 제네릭을 쓰는 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Main { public static void main(String[] args){ Collection<String> studentNames = new ArrayList<>(); studentNames.add("wonyoung"); studentNames.add("somin"); studentNames.add(1); // compile error 발생 || Error:(15, 26) java: incompatible types: int cannot be converted to java.lang.String for (Iterator iterator = studentNames.iterator(); iterator.hasNext();) { String name = iterator.next(); System.out.println(name); } } } | cs |
또한 위 같이 제너릭을 쓰게 되면 형변환을 하지 않아도 된다.
물론 개발자가 조심해서 쓴다는 가정 하에 제너릭을 쓰지 않을 수도 있다. 하지만 그래서는 안된다. 무인자 자료형을 쓰면 형 안전성이 사라지고, 제네릭의 장점 중 하나인 표현력(expressiveness) 측면에서 손해를 보게된다.
(그런데 자바는 형 안전성을 반드시 유지해야하나? 형 안전성이 유지가 안되는 Javascript는 다른 언어라고 이해하면 되나?)
무인자 자료형이 아직도 자바에서 지원되는 이유는 자바에서 제네릭을 도입하면서 다음단계로 넘어갈때 이미 너무나도 많은 양의 코드가 제네릭 없이 구현된 상태였기 때문이다.
또한 제네릭에서 아래와 같이 한정적 자료형으로 사용도 가능하다. 이건 Super class를 상속받은 class만 들어 올 수 있고 Casting을 Super class으로 해준다.
1 2 3 4 5 6 | public static void printAll(ArrayList<? extends Superclass> list) { for(Childclass u : list){ System.out.println(u); } } | cs |
End of Document
'개발이야기 > Effective Java' 카테고리의 다른 글
[Effective Java]가능하면 제네릭 자료형으로 만들 것 (0) | 2017.04.16 |
---|---|
[Effective Java]무점검 경고(unchecked warning)를 제거하라 (0) | 2017.04.16 |
[Effective Java]자료형을 정의할 때 표식 인터페이스를 사용하라 (259) | 2017.03.10 |
[Effective Java]Override 어노테이션은 일관되게 사용하라 (250) | 2017.03.10 |
[Effective Java]작명 패턴 대신 어노테이션을 사용하라 (575) | 2017.03.10 |
[Effective Java]int 상수 대신 enum을 사용하라 (1100) | 2017.03.10 |