Language/JAVA

[이것이 자바다].ch12.제네릭

JSJH._. 2025. 12. 29. 16:46

[이것이 자바다] 12. 제네릭

안녕하세요. 이번에는 자바의 제네릭에 대해 정리한 내용입니다. 제네릭은 배워두면 정말 유용합니다.


1. 제네릭(Generic) 필요성

  • 제네릭이 없던 시절에는 자바의 컬렉션에 모든 종류의 객체를 저장하기 위해 Object 타입을 사용했습니다.
// 제네릭이 없던 시절
List list = new ArrayList();
list.add("hello"); // String을 넣고
String str = (String) list.get(0); // 꺼낼 때는 String으로 강제 형변환 필요

list.add(100); // 실수로 Integer를 넣어도 컴파일 에러가 발생하지 않음
Integer i = (Integer) list.get(1);
  1. 불편함: 객체를 꺼낼 때마다, 원래 타입을 기억해서 매번 강제 타입 변환을 해야 합니다.
  2. 치명적인 위험: 실수로 다른 타입의 객체를 넣어도 컴파일러가 잡지 못합니다. (실행 시 ClassCastException 발생 위험)
  • 해결책: 클래스나 인터페이스, 메소드를 정의할 때 타입을 파라미터화하여 컴파일 시점에 구체적인 타입을 지정하게 합니다.

2. 제네릭 타입 <T>

  • 클래스 또는 인터페이스 이름 뒤 <T>와 같은 타입 파라미터를 붙여 선언합니다. (T는 실제 사용될 타입으로 대체됨)
  • 목적: 클래스 내부에서 사용될 데이터의 타입을 객체를 생성할 때 결정하여 코드의 안정성을 높입니다.
public class Box<T> {
    private T item;

    public T getitem() {
        return item;
    }
    public void setitem(T item) {
        this.item = item;
    }
}

// 사용할 때 구체적인 타입을 지정 (타입 아규먼트)
Box<String> stringBox = new Box<>(); // String만 담을 수 있는 Box
stringBox.setitem("문자열");
String result = stringBox.getitem(); // 강제 형변환이 필요 없음

3. 제네릭 메소드 <T, R>

  • 리턴 타입 앞에 <T, ...>와 같이 독자적인 타입 파라미터를 가지는 메소드입니다.
  • 특징: 클래스가 제네릭 타입이 아니어도, 특정 메소드만 독립적으로 제네릭 적용 가능합니다.
public class Util {
    // T 타입의 Box를 받아 그 안의 내용을 리턴하는 제네릭 메소드
    public static <T> T unboxing(Box<T> box) {
        T item = box.getitem();
        return item;
    }
}

4. 제한된 타입 파라미터

  • <T extends 상위타입>
    • 제네릭에 사용될 수 있는 타입을 특정 클래스 또는 그 자식 클래스들로만 제한합니다.
  • 복적
    1. 타입 제한: 아무 타입이나 들어오는 것을 막고, 의도한 타입 계층 내의 객체만 받도록 강제합니다.
    2. 메소드 접근: 상위 타입에 선언된 메소드를 제네릭 코드 안에서 안전하게 사용 가능합니다.
// Number 클래스 또는 그 자식(Integer, Double 등)만 타입으로 받을 수 있음
public static <T extends Number> int compare(T t1, T t2) {
    // T가 Number의 자식임이 보장되므로, Number의 메소드인 doubleValue()를 안전하게 호출 가능
    double v1 = t1.doubleValue();
    double v2 = t2.doubleValue();
    return Double.compare(v1, v2);
}

5. 와일드카드 타입

  • <?>, <? extends ...>, <? super ...>
    • 제네릭 타입을 메소드의 매개변수나 리턴 타입으로 사용할 때, 구체적인 타입 대신 사용할 수 있는 범용적인 표현입니다.
  • 목적: 제네릭을 사용하는 코드의 유연성을 극대화하기 위함입니다.
  • <?>: 타입에 제한이 없음. 어떤 타입이든 매개변수로 받을 수 있습니다. (주로 읽기 전용)
  • <? extends 상위타입>: 상위타입 또는 그 자식 타입들만 가능합니다.
  • <? super 하위타입>: 하위타입 또는 그 부모 타입들만 가능합니다.

핵심 포인트

  • 제네릭의 존재 이유
    • 컴파일 시점의 타입 안정성 확보
    • 컴파일 에러로 사전에 문제 발견 가능
  • <T extends Number>
    • 제네릭 범위 제한 (Number이거나 그 자식만 허용)
  • 와일드카드(?)
    • 메소드의 매개변수 등에서 다양한 제네릭 타입을 유연하게 받기 위해 사용

감사합니다.