[JAVA] 제네릭(Generic)

이연우·2025년 7월 9일

TIL

목록 보기
4/100

🔤 제네릭이란?

  • 객체를 생성할 때 실제 사용할 타입을 지정할 수 있도록 하는 기능
  • 즉, 여러 타입에 대해 재사용 가능한 코드를 작성할 수 있게 도와주는 문법

✅ 제네릭을 사용하는 이유
1. 타입 안정성(type safety) 확보

  • 컴파일 시 타입 체크를 해서 잘못된 타입 사용 방지
  • ex) List<String>Integer를 넣으면 컴파일 오류 발생

2. 형변환(casting) 없이 사용 가능

  • 제네릭을 사용하면 꺼낼 때 따로 형변환할 필요가 없음

3. 코드 재사용성 증가

  • 다양한 타입을 다룰 수 있는 범용 코드 작성 가능

⚠️ 제네릭이 없는 경우

재사용 불가

  • 특정 타입으로 고정되어 있어 재사용이 어려움
  • 다시 사용하려면 다른 클래스를 만들어야 함 → 낮은 유연성
public class Box {
    private Integer item; // ⚠️ Integer 타입으로 고정

    public Box(Integer item) { // ⚠️ Integer 타입으로 고정
        this.item = item;
    }

    public Integer getItem() {
        return this.item;
    }
}
public class Main {
    public static void main(String[] args) {
        // ✅ Integer 타입 박스
        Box box1 = new Box(100);

        // ❌ String 타입을 저장하려면 새로운 클래스를 만들어야 함
        Box box2 = new Box("ABC"); 

    }
}

타입 안정성 보장 불가

  • 사용 시 형 변환이 발생하고 실수로 잘못된 타입을 사용하면 런타임 오류 발생
  • 잘못된 다운캐스팅 활용 시 → ClassCastException
public class ObjectBox {
    private Object item; // ⚠️ 다형성: 모든 타입 저장 가능 (하지만 안전하지 않음)

    public ObjectBox(Object item) {
        this.item = item;
    }

    public Object getItem() {
        return this.item;
    }
}
public class Main {
    public static void main(String[] args) {
        // ✅ ObjectBox 사용
        ObjectBox objBox = new ObjectBox("Hello");
        String str = (String) objBox.getItem(); // 형변환 필요
        System.out.println("objBox 내용: " + str); // Hello

        // ⚠️ 실행 중 오류 발생 (잘못된 다운 캐스팅: ClassCastException)
        objBox = new ObjectBox(100); // 정수 저장
        String error = (String) objBox.getItem(); // ❌ 오류: Integer -> String 
        System.out.println("잘못된 변환: " + error);
    }
}

🧩 <T> 타입 매개변수

  • 타입을 일반화(Generalize)하기 위해 사용하는 변수 이름
  • 클래스, 메서드, 인터페이스에 붙여서 정해지지 않은 타입을 파라미터처럼 받아서 사용할 수 있게 해 줌

제네릭 클래스에서의 예시

  public class Box<T> {
    private T item;

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

    public T get() {
        return item;
    }
}
  • 여기서 TBox 클래스가 어떤 타입을 담을지 정하고 있다가 객체를 만들 때 결정하게 함
Box<String> stringBox = new Box<>();
stringBox.set("안녕");
String str = stringBox.get();
  • 위의 Box<String>을 쓰면 컴파일러가 TString으로 치환해서 생각

⚡️타입 소거(Erasure)란?

  • 컴파일 시점에 제네릭 타입 정보를 제거하는 과정
    • <T> 타입 매개변수 부분은 Object로 대체됨
    • 필요한 경우 컴파일러가 자동으로 강제 다운캐스팅(cat) 코드를 삽입하여 타입 안정성을 보장

⚙️ 제네릭 메서드(Generic Method)란?

  • 메서드 내부에서 사용할 타입을 유연하게 지정하는 기능
  • 클래스 제네릭 타입과 별개로 독립적인 타입 매개변수를 가짐

🗒️ 예시 코드:

public class GenericBox<T> {

  // 속성
  private T item;

  // 생성자
  public GenericBox(T item) {
      this.item = item;
  }

  // 기능
  public T getItem() {
      return this.item;
  }

		// ⚠️ 일반 메서드 T item은 클래스의 <T>를 따라감
  public void printItem(T item) {
      System.out.println(item);
  }
  
  // ✅ 제네릭 메서드 <S>는 <T>와 별개로 독립적
  public <S> void printBoxItem(S item) { 
      System.out.println(item);
  }
}
public class Main {

  public static void main(String[] args) {
      GenericBox<String> strGBox = new GenericBox<>("ABC");
      GenericBox<Integer> intGBox = new GenericBox<>(100);
      
      // ⚠️ 일반메서드: 클래스 타입 매개변수를 따라감
      // String 데이터 타입 기반으로 타입 소거 발생
      // String 타입의 다운캐스팅 코드 삽입!
      strGBox.printItem("ABC"); // ✅ String만 사용 가능
      strGBox.printItem(100);   // ❌ 에러 발생 
      
      // ✅ 제네릭 메서드: 독립적인 타입 매개변수를 가짐
      // String 타입 정보가 제네릭 메서드에 아무런 영향을 주지 못함
      // 다운캐스팅 코드 삽입되지 않음
      strGBox.printBoxItem("ABC"); //✅ 모든 데이터 타입 활용 가능
      strGBox.printBoxItem(100);   //✅ 모든 데이터 타입 활용 가능
      strGBox.printBoxItem(0.1);   //✅ 모든 데이터 타입 활용 가능
  }
}

다른 관례적인 이름

이름의미
TType(임의의 타입)
EElement(컬렉션 요소)
KKey(Map의 키)
VValue(Map의 값)
NNumber(숫자)

0개의 댓글