제네릭(Generic)은 Java에서 타입을 일반화하여 코드의 재사용성과 안정성을 높이는 기능입니다. 제네릭을 사용하면 컴파일 시점에 타입을 명확히 지정할 수 있어, 타입 안정성을 보장하고 형 변환(casting)의 필요성을 줄일 수 있습니다.
import java.util.ArrayList;
public class WithoutGenerics {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 제네릭 사용 안 함
list.add("Hello");
list.add(10); // 다른 타입도 추가 가능
for (Object obj : list) {
String str = (String) obj; // 명시적 형 변환 필요
System.out.println(str); // 실행 중 오류 가능
}
}
}
ClassCastException
발생 가능.import java.util.ArrayList;
public class WithGenerics {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); // 제네릭 사용
list.add("Hello");
// list.add(10); // 컴파일 오류 발생: Integer는 String 타입에 맞지 않음
for (String str : list) {
System.out.println(str); // 형 변환 불필요
}
}
}
제네릭 클래스를 사용하면 클래스의 필드나 메서드에서 사용하는 타입을 동적으로 설정할 수 있습니다.
public class Box<T> { // T는 타입 파라미터
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
public class Main {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
System.out.println(stringBox.getItem());
Box<Integer> intBox = new Box<>();
intBox.setItem(123);
System.out.println(intBox.getItem());
}
}
<T>
Box<String>
: T
는 String
으로 대체됨.Box<Integer>
: T
는 Integer
로 대체됨.제네릭 메서드는 메서드 수준에서 타입 파라미터를 사용할 수 있습니다.
public class GenericMethodExample {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
public static void main(String[] args) {
String[] strings = {"A", "B", "C"};
Integer[] integers = {1, 2, 3};
printArray(strings); // T는 String으로 대체
printArray(integers); // T는 Integer로 대체
}
}
와일드카드는 제네릭 타입의 경계를 지정하여 보다 유연한 코드를 작성할 수 있게 합니다.
?
(Unbounded Wildcard)public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
<? extends Type>
(Upper Bound Wildcard)public void processNumbers(List<? extends Number> list) {
for (Number num : list) {
System.out.println(num.doubleValue());
}
}
<? super Type>
(Lower Bound Wildcard)public void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
제네릭 타입에 제한을 두어 특정 클래스나 인터페이스를 상속받은 타입만 사용하도록 설정할 수 있습니다.
public class BoundedTypeExample<T extends Number> {
private T value;
public BoundedTypeExample(T value) {
this.value = value;
}
public double square() {
return value.doubleValue() * value.doubleValue();
}
public static void main(String[] args) {
BoundedTypeExample<Integer> intBox = new BoundedTypeExample<>(5);
System.out.println(intBox.square());
BoundedTypeExample<Double> doubleBox = new BoundedTypeExample<>(3.5);
System.out.println(doubleBox.square());
// BoundedTypeExample<String> strBox = new BoundedTypeExample<>("Hello"); // 컴파일 오류
}
}
int
, double
같은 기본 타입을 지원하지 않습니다.Integer
, Double
) 를 사용해야 합니다.