Generic의 필요성
List strList= new ArrayList(); // String의 리스트를 사용할 의도
strList.add(new Integer(0)); // 의도에 맞지 않지만 컴파일러는 모름
String str = (String) strList.get(0); // 의도에 맞지만 런타임 예외 (ClassCastException)
• 기존 (< Java 1.5) Collection 클래스들 (Wrapper 클래스)의 문제점
• opaque 타입을 활용한 구현이기 때문에
• 의도 - 이름, 주석에 의존
• 넣을 때 - 엉뚱한 타입의 객체를 넣게 될 가능성
• 꺼낼 때 - 매번 직접 올바른 타입 캐스팅 필요
• 매번 조심해야
• 상속을 활용한 매개변수 타입을 갖는 메소드 이용시에도 해당
3.
해결방안
• Collection이나 Wrapper클래스의 객체를 생성할 때
컴파일러에게 담을 타입을 한번 알림
• 의도 - 코드에 명확히 나타남
• 넣을 때 - 컴파일러가 타입 체크
• 꺼낼 때 - 컴파일러가 타입 캐스팅
• 타입을 나타내는 매개변수(type parameter) 개념의
도입
• 메소드에 대해서도 적용 가능한 개념
4.
사용법-정의하기
• 제네릭 타입(generic type)
• class X<T> { T 이용 }
• interface X<T> { T 이용 }
• 제네릭 메소드 (generic method)
• <T> Result x(T 이용) { T 이용 } // Result에도 T 이용 가능은 함
• 타입 매개변수 (type parameter)
• simple: <T>, <U,V>
• bounded: <T extends Number>, <T extends Comparable<T>>
5.
사용법-이용하기
• X<String> x= new X<>();
• <String>x(…) 혹은 그냥 x(…) // by 타입 추론
• 타입 인수 (type argument)
• simple: <String>, <String, Integer>
• wildcard:
• unbounded wildcard “?”
• upper bounded wildcard “? extends”
• lower bounded wildcard “? super”
• 형인자 타입 (parameterized type)
• 제네릭 타입의 타입 매개변수에 실제 타입 인수를 지정한 결과 타입
• 제네릭 타입의 호출(invocation of generic type)이라고도 말함
6.
generic type은 불변형
•T2가 T1의 하위 타입인 경우 (T2 is-a T1)
• 타입 매개변수 T를 갖는 타입 F를 F<T>로 표현할 때
• F<T2> is-a F<T1> 관계이면 F<T>는 공변형(covariant)
• F<T1> is-a F<T2> 관계이면 F<T>는 반?변형(contravariant)
• F<T1>과 F<T2>에 is-a 관계가 없으면 F<T>는 불변형(invariant)
• (Ex)
• Integer is-a Number
• List<Number>와 List<Integer> 사이에 is-a 관계가 없다 -> generic type is
invariant
• Integer[] is-a Number[] -> array is covariant
7.
wildcard
• (문제) 제네릭타입의 불변성은 이를 매개변수로 사용하는 메소드의 재사용성을 크게 제
한
• (해결) 메소드 매개변수를 선언할 때 타입 인수로 “?”를 지정하여 임의의 타입 인수를 나
타낼 수 있음
• (결과) 임의의 타입 인수를 지정한 해당 제네릭 타입의 객체를 인수로 지정하여 메소드 호
출 가능
• 타입 인수로 wildcard 지정 가능한 곳
• 멤버 변수 / 지역 변수 /반환 타입 선언
• 타입 인수로 wildcard 지정 불가능한 곳
• 제네릭 메소드 호출 / 제네릭 타입의 객체 생성 / 상위타입 (class X extends Y<?> {})
• unbounded wildcard “?”: 임의의 타입 인수와 매칭
• upper bounded wildcard “? extends Upper”: Upper 이하의 임의의 타입 인수와 매칭
• lower bounded wildcard “? super Lower”: Lower 이상의 임의의 타입 인수와 매칭
8.
PECS - wildcard사용 가
이드
• Producer Extends Consumer Super
• 제네릭 메소드의 인자가 메소드 내에서
• 소스/생산자로 사용되는 경우 —> “? extends”를 사용
• 타겟/소비자로 사용되는 경우 —> “? super”를 사용
• 일반적으로 유용하고 견고한 API를 만드는데 도움
• stream 오퍼레이션, 작업 변수 활용, Robustness rule 경험칙
• receive, 읽기: UpperBounded src —> tmp // 관대하게 받아서
• send, 쓰기: tmp —> LowerBounded target // 엄격하게 처리
9.
타입 소거• 타입소거 (type erasure) - Java에서 generic type을 구현한 방법/기법
• 컴파일러는 타입 매개변수와 타입 인수 정보를 활용하여 타입 체크, 타입 캐
스팅, 브릿지-메소드 생성후 generic 관련 타입 정보를 제거하고 컴파일을
수행한다
• 생성된 바이트코드에는 타입 매개변수나 타입 인수 정보가 포함되지 않는
다
• 브릿지-메소드
• 제네릭 타입의 하위 클래스에서 타입 매개변수를 사용하는 메소드를
override한 경우
• 상위 클래스에 대한 타입 소거 결과 메소드의 signature가 변경되어
override 관계가 없어질 수 있다
• 이를 해결 하기 위해 컴파일러가 필요에 맞게끔 하위 클래스에 생성하는 메
소드
• 일반적으로 의식할 필요는 없으나 stack trace에서 관찰된다. 놀라지 말라
10.
실체화 불가 타입(Non-
reifiable)
• 실행시점에 타입에 대한 정보가 완전하지 않음, 타입 소거의 결과
• 실체화 가능 타입
• primitive types
• non-generic types
• raw types: List
• invocations of unbound wildcards: List<?>
• 실체화 불가 타입
• <?> 이외의 generic type 호출: List<String>, List<? extends Number>, List<? super
Integer>
• 런타임에 JVM은 실체화 불가 타입들을 서로 구분할 수 없다
• 제네릭의 제약 사항중 많은 부분이 이에 기인함 (다음 페이지)
11.
제약사항
• 기본 타입을타입 인수로 사용할 수 없다: <int>
• 타입 매개변수로
• 객체 생성할 수 없다: new T
• static 필드를 선언할 수 없다: static T
• 예외를 catch할 수 없다: catch (T e) — 반면, throws 구문에는 사용 가능하다: void x() throws T
• parameterized type으로
• 타입 테스트할 수 없다: instanceof List<Integer>
• 타입 캐스팅할 수 없다: (List<Integer>) x
• 배열 생성할 수 없다: new List<Integer>[]
• 타입 소거후 메소드 signature가 동일하게 되는 오버로딩은 불가능하다
• void x(List<String> strList) {}
• void x(List<Integer> intList) {}
• 제네릭 예외 클래스를 만들 수 없다: class MyException<T> extends Exception {}