제네릭(Generic) 은 클래스나 메서드를 정의할 때 데이터 타입을 일반화시켜서 여러 타입을 다룰 수 있게 만드는 문법입니다. 즉, 타입을 파라미터화해서 미리 정하지 않고 나중에 사용할 때 지정할 수 있도록 합니다. 타입을 저장할 수 있는 변수 라는 의미에서 타입변수 라고 부르기도 합니다.
classBox<T>{// T에는 Integer, String등 다양한 타입이 들어갈 수 있음.privateT item;publicvoidsetItem(Titem){this.item= item;}publicTgetItem(){return item;}}
위 예제에서 T는 타입 파라미터입니다. Box<Integer>, Box<String>처럼 타입을 지정해서 사용할 수 있습니다.
2. 제네릭의 탄생 배경
Java에서는 JDK 1.5부터 제네릭이 도입되었습니다.
제네릭 이전에는 Object를 사용해서 모든 타입을 처리했지만, 이는 잘못된 타입을 저장하거나 꺼낼 때 런타임 오류가 발생할 수 있었습니다.
위와 같은 문제를 방지하기 위해 컴파일 시점에 타입을 검사할 수 있도록 제네릭이 도입되었습니다.
동일한 기능을 여러 타입별로 구현하지 않아도 되기 때문에, 코드가 간결해지고 재사용성이 높아졌습니다.
즉, 제네릭을 사용하면 타입 안정성을 보장하고 코드 재사용성을 높일 수 있습니다.
3. 제네릭 사용 방법
① 클래스에서 제네릭 사용
② 메서드에서 제네릭 사용
4. 와일드카드(?)
제네릭에서는 와일드카드를 사용하여 보다 유연한 타입 지정이 가능합니다. 와일드카드는 ? 기호로 표현되며, 주로 메서드의 매개변수 타입에 사용됩니다.
와일드카드 종류
표현식
의미
<?>
어떤 타입이든 가능 (Unbounded wildcard)
<? extends T>
T 또는 T의 하위 타입만 가능 (Upper bounded wildcard)
<? super T>
T 또는 T의 상위 타입만 가능 (Lower bounded wildcard)
예시
5. 정리
제네릭이란 컴파일 시점에 타입을 더 엄격하게 검사하여 타입안정성을 높이고, 코드 재사용성을 높이는 도구
List list = new ArrayList();
list.add("Hello");
list.add(123); // 의도치 않은 값도 들어갈 수 있음
String s = (String) list.get(0); // 캐스팅 필요 & classCast에러 발생
class Box<T> {
private T item;
public void set(T item) { this.item = item; }
public T get() { return item; }
}
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get();
public class Util {
public static <T> void print(T item) {
System.out.println(item);
}
}
Util.print("Hello");
Util.print(123);
public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
// Number와 Number의 자식 타입만 매개변수로 받음.
public void sumList(List<? extends Number> list) {
double sum = 0;
for (Number n : list) {
sum += n.doubleValue();
}
System.out.println("총합: " + sum);
}