BE/Java

[Java] Generic 제너릭

baek-dev 2025. 1. 4. 13:52

Java의 클래스, 메서드, 인터페이스에서 사용할 데이터 타입을 일반화하여 유연성과 코드 재사용성을 높이기 위한 기능

타입을 미리 지정하지 않고 나중에 사용할 때 타입을 명시적으로 정의하는 방법

 

사용하는 이유

1. 타입 안정성 보장

  • 잘못된 타입을 사용하는 실수를 컴파일 시점에서 방지
  • 예 : String만 들어가야 할 리스트에 Integer가 들어가지 않도록 방지

2. 형변환의 간소화

  • 제너릭을 사용하면 데이터 타입을 미리 명시하므로 명시적 형변환이 필요없음
  • 코드가 간결하고 가독성이 높아짐

3. 코드 재사용성

  • 다양한 데이터 타입에 대해 동일한 로직을 처리하는 코드를 작성 가능

제너릭 기본 문법

제너릭 클래스 :

// T는 타입 파라미터로, 나중에 실제 데이터 타입으로 대체됨
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

Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);
System.out.println(integerBox.getItem()); // 출력: 123

 

제너릭 메서드 :

public class GenericMethodExample {
    // 제너릭 메서드 정의 (타입 파라미터 T)
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        String[] stringArray = {"A", "B", "C"};
        Integer[] intArray = {1, 2, 3};

        printArray(stringArray); // 출력: A B C
        printArray(intArray);    // 출력: 1 2 3
    }
}

 

제너릭 인터페이스 :

public interface GenericInterface<T> {
    void perform(T item);
}

// 구현 클래스
class StringProcessor implements GenericInterface<String> {
    @Override
    public void perform(String item) {
        System.out.println("Processing: " + item);
    }
}

// 사용
GenericInterface<String> processor = new StringProcessor();
processor.perform("Hello"); // 출력: Processing: Hello

타입 파라미터

타입 파라미터 의미
T Type (일반적인 타입)
E Element (컬렉션 요소)
K Key (맵의 키)
V Value (맵의 값)
N Number (숫자 타입)

제너릭에서 와일드카드(?)

와일드카드는 특정 타입이 아닌 모든 타입을 나타내기 위해 사용

종류

1. 제한없는 와일드카드(?)

  • 어떤 타입도 받을 수 있음
public void printBox(Box<?> box) {
    System.out.println(box.getItem());
}

2. 상한 제한 와일드카드(<? extends Type>)

  • 특정 클래스나 그 하위 클래스를 제한
public void printNumbers(List<? extends Number> list) {
    for (Number number : list) {
        System.out.println(number);
    }
}

3. 하한 제한 와일드카드(<? super Type>)

  • 특정 클래스나 그 상위 클래스를 제한
public void addElements(List<? super Integer> list) {
    list.add(10); // Integer 또는 그 하위 타입 추가 가능
}

한계

1. 기본타입 사용 불가

  • 제너릭은 기본타입(int, double 등)을 사용할 수 없으며, 대신 래퍼클래스(Integer, Double)를 사용해야함
List<int> list; // 오류
List<Integer> list = new ArrayList<>(); // 가능

 

2. 런타임 소거(Type Erasure)

  • 컴파일 후 제너릭 정보는 제거되므로 런타임에서는 원시타입(Raw Type)처럼 작동
  • 따라서 런타임에는 제너릭 타입 정보를 알 수 없음
if (list instanceof List<String>) { // 오류
    // 실행 불가: 제너릭 타입은 런타임에 소거됨
}

 

 

3. 배열 생성 불가

T[] array = new T[10]; // 오류

요약

1. 제너릭(Generic)은 타입을 일반화하여 유연성을 높이고 코드의 재사용성을 강화

2. 장점:

  • 타입 안전성 보장
  • 불필요한 형변환 제거
  • 다양한 타입에 대해 동일한 로직 처리 가능

3. 주요 용법:

  • 제너릭 클래스, 제너릭 메서드, 제너릭 인터페이스
  • 와일드카드(?, <? extends>, <? super>)

4. 활용:

  • 컬렉션 프레임워크, 스프링의 JPA, 사용자 정의 클래스 등에 광범위하게 사용

 

 

 

 

출처 : ChatGPT

 

'BE > Java' 카테고리의 다른 글

[Java] Jackson 라이브러리  (0) 2025.01.07
[JPA] N + 1 문제  (0) 2025.01.06
[Java] enum  (1) 2025.01.01
[Java] @IdClass  (0) 2024.12.31
[Java] @Builder, @ToString, @EqualsAndHashCode, @Singular  (0) 2024.12.23