Java - 제네릭 타입

오스카·2024년 12월 20일

JAVA 공부

목록 보기
3/3

Java의 제네릭 타입은 클래스, 인터페이스, 메서드를 정의할 때 타입을 매개변수화할 수 있게 해주는 기능이다. 이를 통해 코드의 재사용성과 타입 안정성을 높일 수 있다.

장점

  1. 타입 안정성: 컴파일 시점에 타입 체크를 수행하여 런타임 오류를 방지한다.
  2. 코드 재사용: 다양한 데이터 타입에 대해 동일한 코드를 사용할 수 있다.
  3. 타입 캐스팅 제거: 명시적인 타입 캐스팅이 필요 없어 코드가 간결해진다.

정의 방식

제네릭 클래스

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>와 호환되지 않는다.)

profile
몸이 셋 쯤 되고 싶은 초보 개발자

0개의 댓글