목차
제네릭
다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일시의 타입 체크를 해주는 것.
데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법
제네릭의 장점
- 의도하지 않은 타입의 객체가 저장되는 것을 막고, 저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 잘못 형변환되어 발생할 수 있는 오류를 줄여준다. 즉, 타입 안정성을 제공하는 것이다.
- 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해진다.
제네릭 클래스
선언하는 방법
public class Box {
Object item;
void setItem(Object item){
this.item = item;
}
Object getItem(){
return item;
}
}
다음과 같이 클래스 Box 가 정의되어 있다고 하자. 이 클래스를 제네릭 클래스로 변경하려면 클래스 옆에 <T> 를 붙이면 된다. 그리고 Object 를 모두 T 로 변경한다. 여기서 T 는 타입 변수(type variable) 의 첫 글자로, 꼭 T로 사용하지 않아도 Element(요소) 의 첫 글자인 E, Key 의 첫 글자인 K를 사용해도 무방하다. 그저 상황에 맞게 사용하면 된다.
public class Box<T> {
T item;
void setItem(T item){
this.item = item;
}
T getItem(){
return item;
}
}
객체 생성
이제 제네릭 클래스가 된 Box 클래스의 객체를 생성할 때는 다음과 같이 참조변수와 생성자에 타입 T대신에 사용될 실제 타입을 지정해주면 된다.
Box<String> b = new Box<String>(); //타입 T 대신 실제 타입 지정
b.setItem(new Object()); // String 외 타입은 지정 불가하므로 에러
b.setItem("ABC"); // String 타입이므로 가능
String item = b.getItem(); // String으로 형변환 하지 않아도 된다.
이처럼 어떤 타입이든 한 가지 타입을 정해서 담을 수 있다.
그리고 참조변수와 생성자에 대입된 타입이 일치해야한다.
두 타입이 상속관계에 있어도 마찬가지다.
Apple이 Fruit 의 자손이라고 가정해보자.
Box<Fruit> appleBox = new Box<Apple>(); //대입된 타입이 다르기 때문에 에러
Box<Apple> appleBox = new Box<Apple>(); // 대입된 타입이 같기 때문에 OK.
Box<Apple> appleBox = new FruitBox<Aplpe>(); // FruitBox는 Box의 자손이라고 했을 때 OK. 다형성
타입 제한하기
만약 타입 매개변수 T에 지정할 수 있는 타입을 제한하려면 extends 키워드를 사용하면 된다.
특정 타입의 자손들만 대입할 수 있게 제한할 수 있다.
class FruitBox<T extends Fruit>{
ArrayList<T> list = new ArrayList<T>();
...
}
제네릭 용어
- Box<T> : 제네릭클래스. T의 Box 또는 T Box 라고 읽는다.
- T : 타입변수 또는 타입 매개변수
- Box : 원시타입
제네릭 타입의 제거
컴파일 후에 Box<String> 과 Box<Integer>는 모두 이들의 원시타입인 Box로 바뀐다. 즉, 컴파일된 파일(*.class)에는 제네릭 타입에 대한 정보가 없는 것이다.
그 이유는 이전의 소스 코드와의 호환성을 유지하기 위해서이다.
제네릭 메서드
메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라고 한다.
선언하는 방법
제네릭 타입의 선언 위치는 반환타입 바로 앞이다.
public <T> void getName(){ ... }
와일드카드
와일드 카드는 기호 '?' 로 표현하며 어떠한 타입도 될 수 있다.
사용법
extends 와 super 로 상한(upper bound) 과 하한(lower bound)를 제한할 수도 있다.
- <T> : 제한없음. 모든 타입 가능. <? extends Object>와 동일
- <? super T> : 와일드카드의 하한 제한. T와 그 조상들만 가능하다.
- <? extends T> : 와일드카드의 상한 제한. T와 그 자손들만 가능하다.
void makeJuice(FruitBox<? extends Fruit> box){
...
}
매개변수의 타입을 FruitBox<? extends Object> 로 지정했다.
이 메서드의 매개변수로 FruitBox<Fruit> , FruitBox<Apple>,FruitBox<Grape> 모두 가능하다.
참고
자바의 정석 남궁성