Java의 제네릭 타입은 클래스, 인터페이스, 메서드를 정의할 때 타입을 매개변수화할 수 있게 해주는 기능이다. 이를 통해 코드의 재사용성과 타입 안정성을 높일 수 있다.
public class Box<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
여기에서의 T는 타입 매개변수이다.
public <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
상한 경계: <T extends SuperClass>로 정의하여 특정 클래스의 하위 타입만 허용할 수 있다.
하한 경계: <T super SubClass>로 정의하여 특정 클래스의 상위 타입만 허용할 수 있다.
와일드카드: ?를 사용하여 알 수 없는 타입을 표현할 수 있다.
타입에 주로 사용하는 키워드
E - Element
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
@Test
public void testGenericMethod() {
Integer[] intArray = {1, 5, 3, 7, 2};
String[] strArray = {"apple", "banana", "cherry"};
assertEquals(Integer.valueOf(7), findMax(intArray));
assertEquals("cherry", findMax(strArray));
}
public static <T extends Comparable<T>> T findMax(T[] arr) {
// findMax 메서드 구현
}
public <T extends Number> double sumOfList(List<T> list) {
return list.stream().mapToDouble(Number::doubleValue).sum();
}
@Test
public void testSumOfList() {
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
assertEquals(6.0, sumOfList(intList), 0.001);
assertEquals(6.6, sumOfList(doubleList), 0.001);
}
만약 제네릭을 사용하지 않고 코드를 작성하면, 아래와 같은 문제가 발생할 수 있다.
public double sumOfList(List<Number> list) {
return list.stream().mapToDouble(Number::doubleValue).sum();
}
위처럼 List<Number>로 타입을 고정시키면, List<Integer>나 List<Double> 같은 구체적인 타입은 전달할 수 없다. 왜냐하면 List<Integer>는 List<Number>의 서브타입이 아니기 때문이다.
(Java에서 제네릭은 공변성을 지원하지 않으므로 List<Number>는 List<Integer>와 호환되지 않는다.)