하나의 값이 여러 타입을 가질 수 있도록 하는 방법을 의미한다. 메서드나 *컬렉션 클래스에 컴파일 시의 타입 체크를 해주기 때문에, 객체의 *타입 안정성을 높이고 형변환의 번거로움이 줄어든다.
컬렉션 클래스?
데이터를 효율적으로 저장하고 관리할 수 있도록 제공되는 자료구조 클래스를 의미한다.
대표적으로 자바에서 List, Set, Queue 등이 존재한다.
타입 안정성을 높인다?
제네릭 타입은 클래스와 메서드에서 선언할 수 있다.
class Test {
Object item;
void setItem(Object item) { this.item = item; }
Object getItem() { return item; }
}
위 클래스를 제네릭 클래스로 바꿔보자.
class Test<T> {
T item;
void setItem(T item) { this.item = item; }
T getItem() { return item; }
}
Test<T>에서 T를 타입 변수라고 하며, 타입 변수는 T가 아닌 다른 것을 사용해도 된다
제네릭 클래스를 사용하여 객체를 생성할 때는 참조변수와 생성자에 타입 T 대신 사용할 실제 타입을 지정해주어야 한다.
Test<String> t = new Test<String>();
t.setItem(new Object()); // error! String이외의 타입 지정 불가
t.setItem("item1"); // OK
//String item = (String) t.getItem(); // 형변환이 필요 없음
String item = t.getItem();
매개변수에 과일을 대입하면 잼을 만들어서 반환하는 Maker라는 클래스가 있고, 이 클래스는 과일을 잼으로 만들어서 반환하는 makeJam()이라는 static 메서드가 다음과 같이 정의되어 있다고 하자.
class Maker {
static Jam makeJam(FruitBox<Fruit> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Jam(tmp);
}
}
static 메서드에는 타입 매개변수 T를 매개변수에 사용할 수 없다.
해당 코드를 통해서는 Fruit 타입의 클래스만 makeJam 메서드를 사용할 수 있다. 따라서, 여러 가지 타입의 매개변수를 갖는 makeJam()을 만들 수밖에 없다.
static Jam makeJam(FruitBox<Fruit> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Jam(tmp);
}
static Jam makeJam(FruitBox<Apple> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Jam(tmp);
}
그러나 위 코드에서는 컴파일 에러가 발생한다.
제네릭 타입이 다른 것만으로는 오버로딩이 성립하지 않기 때문!
이럴 때 사용하기 위해 고안된 것이 바로 '와일드 카드' 이다. 와일드 카드는 ?로 표현하며, 어떠한 타입도 될 수 있다.
makeJam을 와일드 카드를 사용하여 여러 개의 타입을 매개변수로 받을 수 있다.
static Jam makeJam(FruitBox<?> box extends Fruit) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Jam(tmp);
}
와일드 카드를 통해 Friut 타입과 Fruit의 자손들을 매개변수로 사용가능하다.
메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라 한다.
static <T> void osrt(List<T> list, Comparator<? super T> c)
static 멤버에는 타입 매개변수를 사용할 수 없지만, 이처럼 메서드에 제네릭 타입을 선언하고 사용하는 것은 가능하다.
- 메서드에 선언된 제네릭 타입은 지역 변수를 선언한 것과 같다
앞서 봤던 makeJam()을 제네릭 메서드로 바꾸면 다음과 같다.
static <T extends Fruit> Jam makeJam(Fruit<T> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Jam(tmp);
}
참고
남궁 성. 『Java의 정석』. 도우출판, 2016
https://st-lab.tistory.com/153