Generic

hoon·2025년 1월 21일

JAVA

목록 보기
13/21

1. 제네릭(Generics)이란?

  • Java에서 컴파일 시 타입 안정성을 제공하고, 타입 캐스팅을 줄이는 데 도움을 주는 기능.
  • 클래스, 메서드, 인터페이스에서 데이터 타입을 일반화하여 코드 재사용성을 높임.

2. 제네릭 사용 이유

  1. 타입 안정성 제공:
    • 잘못된 타입 사용을 컴파일 단계에서 차단.
    • 런타임 에러를 줄임.
  2. 코드 재사용성 증가:
    • 데이터 타입에 구애받지 않고 다양한 타입에 대해 동작하는 코드를 작성 가능.
  3. 캐스팅 감소:
    • 불필요한 명시적 형변환(casting) 제거.

3. 제네릭의 주요 문법

3.1 제네릭 클래스

  • 클래스 정의에 타입 매개변수(Type Parameter)를 사용.
// 제네릭 클래스 선언
public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

// 사용
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
System.out.println(stringBox.getItem()); // 출력: Hello

3.2 제네릭 메서드

  • 메서드에만 제네릭을 적용하여 호출 시 타입 지정 가능.
// 제네릭 메서드 선언
public static <T> void printArray(T[] array) {
    for (T item : array) {
        System.out.print(item + " ");
    }
}

// 사용
Integer[] intArray = {1, 2, 3};
String[] strArray = {"A", "B", "C"};
printArray(intArray); // 출력: 1 2 3
printArray(strArray); // 출력: A B C

3.3 제네릭 인터페이스

  • 인터페이스에서도 제네릭을 사용할 수 있음.
// 제네릭 인터페이스 선언
public interface Pair<K, V> {
    K getKey();
    V getValue();
}

// 구현 클래스
class KeyValue<K, V> implements Pair<K, V> {
    private K key;
    private V value;

    public KeyValue(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

// 사용
Pair<Integer, String> pair = new KeyValue<>(1, "One");
System.out.println(pair.getKey() + ": " + pair.getValue()); // 출력: 1: One

4. 제네릭의 한정 (Bounded Type Parameters)

4.1 상위 클래스 또는 인터페이스로 한정

  • 타입 매개변수에 특정 타입 또는 그 하위 클래스만 허용.
// Number 클래스와 그 하위 클래스만 허용
public class Calculator<T extends Number> {
    public double square(T number) {
        return number.doubleValue() * number.doubleValue();
    }
}

// 사용
Calculator<Integer> calc = new Calculator<>();
System.out.println(calc.square(5)); // 출력: 25.0

5. 와일드카드 (Wildcards)

  • 불특정한 타입을 의미.
  • 제네릭의 타입 파라미터를 모를 때 사용.
public static void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

// 사용
List<String> stringList = List.of("A", "B", "C");
printList(stringList); // 출력: A B C

6. 제네릭의 제한 사항

  1. 기본 타입 사용 불가:

    • 제네릭 타입에는 int, double 같은 기본 타입 사용 불가.
    • 대신 래퍼 클래스 (Integer, Double) 사용.
    List<int> list = new ArrayList<>(); // 컴파일 에러
    List<Integer> list = new ArrayList<>(); // 올바름
    
  2. 정적 변수와 함께 사용 불가:

    • 제네릭 타입은 클래스의 정적 멤버와 함께 사용할 수 없음.
    public class GenericClass<T> {
        private static T value; // 컴파일 에러
    }
    
  3. 런타임 시 타입 소거(Type Erasure):

    • 제네릭 타입 정보는 컴파일 시에만 유지되며, 런타임에는 제거됨.
    List<String> stringList = new ArrayList<>();
    List<Integer> intList = new ArrayList<>();
    System.out.println(stringList.getClass() == intList.getClass()); // 출력: true
    

7. 제네릭의 장점

  • 컴파일 단계에서 타입 안정성 제공.
  • 불필요한 형변환 제거.
  • 코드의 재사용성과 가독성 증가.

8. 주요 활용 사례

  • 컬렉션 클래스: List, Set, Map 등.
  • 유틸리티 클래스: Optional, Comparator, Stream API 등.
  • 커스텀 데이터 구조: Stack, Queue, Pair 등의 구현.

9. 결론

  • 제네릭은 타입 안정성과 재사용성을 높이는 강력한 도구다.
  • 올바르게 사용하면 코드를 간결하고 안전하게 작성할 수 있다.
  • 그러나 타입 소거와 같은 한계를 이해하고 적절히 활용해야 한다.

0개의 댓글