Kim-Baek 개발자 이야기

제네릭 타입 소거 (컴파일/런타임) 본문

개발/java basic

제네릭 타입 소거 (컴파일/런타임)

김백개발자 2024. 11. 16. 09:39
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
// stringBox.setValue(10); // 컴파일 오류: 타입 불일치

1. 제네릭 타입 소거 (Generic Type Erasure)

자바에서 제네릭(Generic)은 코드의 재사용성과 타입 안전성을 증가시키는 중요한 기능이지만, 자바 컴파일러는 **타입 소거(Erasure)**를 사용하여 제네릭을 처리합니다. 타입 소거란 컴파일 시점에서 제네릭 타입 정보를 제거하는 과정을 말합니다. 이 때문에 자바의 제네릭은 런타임에 타입 정보를 알 수 없으며, 컴파일 타임에만 제네릭이 유효하게 됩니다.

제네릭 타입 소거의 동작 원리

자바 컴파일러는 제네릭을 처리할 때, 제네릭 타입의 인스턴스를 실제 클래스 타입으로 변경합니다. 즉, 제네릭 타입은 **런타임에 원시 타입(raw type)**으로 변환됩니다.

예를 들어, 다음과 같은 코드가 있을 때:

public class Box<T> {
    private T value;

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

    public T getValue() {
        return value;
    }
}

컴파일러는 Box<T>를 처리할 때 제네릭 타입 매개변수 T를 실제 타입으로 대체합니다. 예를 들어 Box<String>은 Box로 컴파일되며, 이때 제네릭 정보는 소거되고, 실제 타입은 런타임에 결정됩니다. T는 실제로 Object로 대체될 수도 있으며, 구체적인 타입이 지정되어 있으면 그 타입으로 바뀝니다.

결과적으로:

  • 컴파일 타임: 제네릭의 타입 정보를 이용해 타입 안전성을 확보합니다.
  • 런타임: 제네릭 타입 정보는 소거되고 원시 타입이 사용됩니다.

타입 소거의 예시

Box<String> box = new Box<>();
box.setValue("Hello");

// 컴파일 타임에는 Box<String> 타입으로 동작하지만,
// 실제로 런타임에서는 타입 정보가 소거되고 Box가 사용됩니다.

2. 컴파일 타임과 런타임

자바에서 제네릭을 사용할 때 컴파일 타임런타임에 중요한 차이가 있습니다. 제네릭 타입 소거는 컴파일 타임에 이루어지고, 런타임에 제네릭 정보가 손실됩니다.

컴파일 타임

  • 타입 안전성 확보: 제네릭을 사용하면 컴파일 타임에 타입 오류를 미리 체크할 수 있습니다.
  • 예시:제네릭을 사용하면 Box<String>에 String만 허용되므로, Box에 다른 타입을 넣으려고 하면 컴파일 타임에 오류가 발생합니다.
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
// stringBox.setValue(10); // 컴파일 오류: 타입 불일치

런타임

  • 타입 정보 손실: 제네릭 타입은 런타임에 소거되므로, 런타임에서는 원시 타입(raw type)만 알 수 있습니다. 즉, 제네릭 타입을 객체에 대한 정보가 없으며, 반사(reflection)나 기타 방법을 통해서는 제네릭 타입을 알 수 없습니다.
  • 예시:Box<String>의 경우 런타임에 Box로만 처리되고, String에 대한 정보는 사라집니다.
Box<String> stringBox = new Box<>();
System.out.println(stringBox.getClass());  // 출력: class Box

3. 제네릭과 타입 소거의 예시

public class Main {
    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>();
        intBox.setValue(10);

        Box<String> strBox = new Box<>();
        strBox.setValue("Hello");

        // 컴파일 타임에는 Box<Integer>, Box<String> 각각에 대한 타입 안전성 보장
        // 하지만 런타임에는 Box만 존재하며, Integer나 String 타입 정보는 사라짐
    }
}

위 코드에서 Box<Integer>와 Box<String>은 컴파일 타임에 타입 안전성을 보장하지만, 런타임에 실제로 어떤 타입이 사용되는지 알 수 없습니다. 이로 인해 타입 정보를 런타임에서 추적할 수 없는 제약이 있습니다.

4. 타입 파라미터의 경계 (Bounded Type Parameters)

제네릭은 타입 파라미터에 대한 경계를 설정할 수 있습니다. 예를 들어, T가 특정 클래스나 인터페이스를 상속한 타입만 허용하도록 할 수 있습니다.

public class Box<T extends Number> { // T는 Number의 하위 타입만 허용
    private T value;
    public void setValue(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }
}

이 경우, T는 Number를 확장한 타입만 가능하므로, Box<Integer>나 Box<Double>은 허용되지만 Box<String>은 허용되지 않습니다. 이러한 경계는 컴파일 타임에 체크됩니다.

결론

  • 제네릭 타입 소거는 자바 컴파일러가 제네릭의 타입 정보를 컴파일 타임에 소거하고, 런타임에는 원시 타입을 사용하도록 하는 메커니즘입니다.
  • 컴파일 타임에 제네릭은 타입 안전성을 제공하지만, 런타임에서는 제네릭 타입 정보가 소거되어 원시 타입만 남습니다.
  • 제네릭은 컴파일 타임에만 타입 검사를 제공하며, 런타임에서는 제네릭 정보가 소거되므로 타입 정보에 접근할 수 없습니다.
반응형
Comments