클래스와 인터페이스 선언에 타입 매개변수를 사용하는 것
→ 제네릭 클래스 / 제네릭 인터페이스
ex) List<E>
제네릭 클래스와 제네릭 인터페이스를 통틀어 제네릭 타입이라고 함
제네릭 타입을 하나 정의하면 그에 딸린 로 타입도 함께 정의됨
→ 로 타입은 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말함
ex) List<E>
의 로 타입은 List
제네릭을 지원하기 전
private final Collection stamps = ...;
for(Iterator i = stamps.iterator(); i.hasNext();){
Stamp stamp = (Stamp) i.next(); // ClassCastException 발생!
stamp.cancel();
}
런타임에 문제를 겪는 코드와 원인을 제공한 코드가 떨어져 있게 되므로 원인 코드를 찾기 어려워짐
제네릭 활용
private final Collection<Stamp> stamps = ...;
컴파일러는 stamps에 Stamp 인스턴스만 넣어야 함을 인지하게 됨
→ 아무 경고가 없다면 의도대로 동작할 것임을 보장할 수 있음
List<Object>
는 모든 객체 타입을 허용한다는 의사를 컴파일러에게 명확하게 전달하는 것List<String>
을 List<Object>
에 넘길 수 없지만 List
에는 List<String>
을 넘길 수 있음 → 타입 안정성을 잃게 됨제네릭 타입을 사용하고 싶지만 실제 타입 매개변수가 무엇인지 신경쓰고 싶지 않을 때는 비한정적 와일드카드 타입을 사용하자
2개의 집합을 받아 공통 원소의 개수를 반환하는 메서드
int numElementsInCommon(Set s1, Set s2) {
int result = 0;
for (Object o1 : s1) {
if(s2.contains(o1) {
result++;
}
return result;
}
int numElementsInCommon(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o1 : s1) {
if(s2.contains(o1) {
result++;
}
return result;
}
Collection<?>
에서는 null외의 어떤 원소도 넣을 수 없음 → 컬렉션의 타입 불변식을 훼손하지 못하게 막음class 리터럴
List.class
, String[].class
, int.class
(✅)List<String>.class
, List<?>.class
(❌)instanceof 연산자
if (o instanceof Set) { // 로 타입
Set<?> s = (Set<?>) o; // 와일드카드 타입
}
instanceof?
instanceof
연산자는 자바에서 객체가 특정 클래스의 인스턴스인지 또는 특정 인터페이스를 구현하는지를 검사하는 데 사용됨
런타임에는 제네릭 타입 정보가 지워지므로 instanceof 연산자는 로타입과 비한정적 와일드카드든 똑같이 동작함
비한정적 와일드카드의 역할이 없어지므로 로 타입을 쓰는 것이 깔끔함