어제 오늘 내일

[Java] 제네릭(Generic) 기본 개념과 예제 본문

IT/Java

[Java] 제네릭(Generic) 기본 개념과 예제

hi.anna 2025. 12. 31. 00:51

Java의 제네릭(Generic)은 클래스나 메서드에서 데이터 타입을 미리 지정하지 않고,
사용하는 시점에 타입을 결정하도록 만드는 기능입니다.
제네릭을 사용하면 타입 안정성이 높아지고, 캐스팅이 필요 없어 코드가 더 깔끔해집니다.

 

1. 제네릭이란?

제네릭은 타입을 파라미터처럼 전달하는 문법입니다.

형식:

클래스명<타입>

예:

List<String> list = new ArrayList<>();

설명

  • String 타입만 저장할 수 있는 리스트
  • 다른 타입을 넣으면 컴파일 단계에서 오류 발생 → 타입 안정성 확보

 

2. 제네릭을 사용하는 이유

✔ 타입 안정성(Type Safety) 향상

잘못된 타입을 넣는 실수를 방지할 수 있습니다.

List<String> list = new ArrayList<>();
list.add(10); // ❌ 컴파일 오류

✔ 캐스팅 불필요

제네릭을 사용하지 않으면 값 꺼낼 때 캐스팅이 필요했습니다.

Object obj = list.get(0);  // 제네릭 미사용 시
String str = (String) obj;

제네릭 사용 시:

String str = list.get(0); // ✔ 자동 타입 결정

✔ 코드 재사용성 증가

하나의 클래스/메서드를 여러 타입에서 재사용할 수 있습니다.

 

3. 제네릭 클래스 기본 예제

class Box<T> {     // T는 타입 파라미터
    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}

사용:

Box<String> box1 = new Box<>();
box1.set("Hello");
System.out.println(box1.get());

Box<Integer> box2 = new Box<>();
box2.set(100);
System.out.println(box2.get());

설명

  • Box<String> → T가 String으로 지정
  • Box<Integer> → T가 Integer로 지정

 

4. 제네릭 메서드

메서드 자체에 타입 파라미터를 선언할 수도 있습니다.

public <T> void print(T item) {
    System.out.println(item);
}

사용:

print("Hello");
print(123);
print(3.14);

설명

  • 타입 파라미터 <T>가 메서드 실행 시점에 자동 결정

 

5. 제네릭 타입 여러 개 사용하기

class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
}

사용:

Pair<String, Integer> p = new Pair<>("age", 20);
System.out.println(p.getKey());   // age
System.out.println(p.getValue()); // 20

 

6. 제네릭 와일드카드(?)

6-1. 어떤 타입이든 허용 (unbounded wildcard)

List<?> list = new ArrayList<String>();

6-2. 상한 제한 (Upper Bound)

List<? extends Number> list;  // Number 또는 자식 타입만 허용

6-3. 하한 제한 (Lower Bound)

List<? super Integer> list;   // Integer 또는 부모 타입만 허용

와일드카드는 “어떤 타입이 들어올지 모르지만 범위를 제한하고 싶을 때” 사용합니다.

 

7. 실전 예제: 저장 타입을 제한한 제네릭 클래스

class NumberBox<T extends Number> { // Number를 상속한 타입만 허용
    private T num;

    public void set(T num) {
        this.num = num;
    }

    public T get() {
        return num;
    }
}

사용:

NumberBox<Integer> n1 = new NumberBox<>();
n1.set(10);

NumberBox<Double> n2 = new NumberBox<>();
n2.set(3.14);

설명

  • String은 Number의 자식이 아니므로 사용 불가

 

8. 정리

  • 제네릭은 타입을 파라미터처럼 전달하는 문법
  • 타입 안정성 증가
  • 캐스팅 필요 없음
  • 재사용성 증가
  • 클래스, 메서드, 인터페이스 등 다양한 곳에서 활용 가능
  • 와일드카드를 사용해 타입 범위를 유연하게 설정 가능

 

 

반응형
Comments