JAVA

[Java] 제네릭 타입 컴파일 Generic type erasure

로춘남 2022. 2. 24. 19:28
728x90


제네릭 타입을 컴파일하면 class 파일에 제네릭이 남아있을까? 

제네릭(Generic)

  • 데이터 타입을 일반화(Generalize)하는 것을 의미.
  • 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시 미리 지정하는 방법.(즉, 컴파일시 미리 타입검사를 수행하는 방법)

→ 클래스나 메소드 내부에서 사용되는 객체의 안정성을 높일 수 있음.

→ 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있음.

 

Row Type

  • 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을때를 의미. 

자바 제네릭이란? ☜ Click!

 

Java Generics Type Erasure(제네릭 타입소거)

구체화 vs 비구체화

  • 구체화 타입(reifiable type) : 자신의 타입 정보를 런타임에도 알고 있는것. 배열이 구체화 타입에 해당된다.
  • 비구체화 타입(non-reify type) : 런타임에는 소거(erasure)가 되기 때문에 컴파일 타임보다 정보를 적게 가지는 것. 제네릭 타입이 비구체화 타입에 해당되며, 제네릭은 컴파일 타임에 타입 체크를 한 뒤 런타임에는 타입을 지우는 방법을 사용하고 있다.

Generics Type Erasure란?

말 그대로 소거(erasure). 원소 타입을 컴파일 타입에만 검사하고 런타임에는 해당 타입 정보를 알 수 없는 것이다. 정리하자면, 컴파일 타입에만 타입 제약 조건을 정의하고, 런타임에는 타입을 제거한다는 뜻이다.

 

자바 컴파일러의 타입 소거

Unbounded Type(<?>, <T>)는 Object으로 변환한다.

// 컴파일 할 때 (타입 소거 전) 
public class Test<T> {
    public void test(T test) {
        System.out.println(test.toString());
    }
}

// 런타임 때 (타입 소거 후)
public class Test {
    public void test(Object test) {
        System.out.println(test.toString());
    }
}

Bound Type(<E extends Comparable>)의 경우 Object가 아닌 Comparable로 변환

// 컴파일 할 때 (타입 소거 전) 
public class Test<T extends Comparable<T>> {
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

// 런타임 때 (타입 소거 후)
public class Test {
    private Comparable data;

    public Comparable getData() {
        return data;
    }

    public void setData(Comparable data) {
        this.data = data;
    }
}

확장된 제네릭 타입에서 다형성 보존을 위해 어떠한 클래스나, 인스턴스를 상속 혹은 구현할때 bridge method를 생성한다.

public class Node<T> {
    public T data;
    public Node(T data) { this.data = data; }
    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

// 컴파일 할 때 (타입 소거 전) 
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

// 런타임 때 (타입 소거 후)
public class MyNode extends Node {

    // Bridge method generated by the compiler
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
    // ...
}

bridge 메서드는 type erasure 이후 Node 클래스에서의 setData 메서드와 같은 이름의 메서드를 가지고 원래의 setData 메서드의 역할을 대신한다.

 

→ 제네릭 타입을 사용할 수 있는 일반 class, interface, method에만 소거 규칙을 적용한다.

→ 타입 안정성 측면에서 필요하면 type casting을 넣는다.


참조: https://velog.io/@guswns3371/%EC%A0%9C%EB%84%A4%EB%A6%AD

https://devlog-wjdrbs96.tistory.com/263

제네릭의 개념(TCP School)

728x90