public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
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());
}
}
위 예제에서 Box<T>
는 제네릭 클래스로, 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.print(element + " ");
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5};
String[] stringArray = {"A", "B", "C", "D"};
printArray(intArray); // 1 2 3 4 5
printArray(stringArray); // A B C D
}
}
위의 예제에서 printArray
메소드는 제네릭 메소드로, 메소드 이름 앞에 <T>
를 사용하여 타입 매갯변수를 정의한다. 이 메소드는 다양한 타입의 배열을 인자로 받아들일 수 있다.
제네릭 타입 매개변수에 제한을 두어 특정 타입의 서브타입만 사용하도록 할 수 있다.
public class BoundedTypeExample {
public static <T extends Number> void printDouble(T number) {
System.out.println(number.doubleValue());
}
public static void main(String[] args) {
printDouble(10); // 10.0
printDouble(10.5); // 10.5
// printDouble("10"); // 컴파일 에러: String은 Number의 서브타입이 아님
}
}
위의 예제에서 T extends Number
는 T
가 Number
클래스의 서브타입이어야 함을 의미한다.
제네릭에서 와일드카드를 사용하여 제네릭 타입의 유연성을 높일 수 있다.
import java.util.List;
public class WildcardExample {
public static void printList(List<?> list) {
for (Object elem : list) {
System.out.print(elem + " ");
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> intList = List.of(1, 2, 3, 4, 5);
List<String> stringList = List.of("A", "B", "C", "D");
printList(intList); // 1 2 3 4 5
printList(stringList); // A B C D
}
}
위의 예제에서 List<?>
는 어떤 타입의 리스트도 인자로 받을 수 있음을 의미합니다.
Java의 제네릭은 코드의 타입 안정성, 재사용성, 가독성을 높이기 위한 강력한 도구이다. 제네릭을 적절히 사용하면 타입 캐스팅에 따른 오류를 줄이고, 보다 유연하고 유지보수하기 쉬운 코드를 작성 할 수 있다.