ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java Generic Compile 동작
    JAVA 2023. 9. 11. 14:42

    Java 의 Generic

    Java에는 Generic이라는 이라는 기능이 있다. Generic 이라는 기능은 JDK 1.5 부터 도입된 기능인데 이전 자바 버전의 코드와의 호환성은 어떤식으로 유지하는걸까? 이를 위해 Type Erasure라는 기능으로 컴파일 시에는 Generic type을 제거하게 된다. 이와 관련하여 간단하게 확인해 보자.

     

    Reifiable 과 Non-Reifiable

    Refiable Type (구체화 타입)

    타입 정보를 런타임시에 알고 구체화 하는것. 컴파일 단계에서 Type Erasure에 의해 지워지지 않는다. 일반적인 타입과 unbound wildecard type 등이 포함된다.

    Non-Reifiable Type (비구체화 타입)

    타입 정보가 런타임때 사라지는것. 컴파일 단계에서 Type Erasure에 의해 타입이 지워진다. 제네릭 타입 파라미터등이 포함된다.

     

    Unbounded / Bounded Type

    Unbounded type

    말그대로 Unbounded(무한한) wildcard type이다. 예를들어 List<?> 같은 형식이다.

    Bounded Type

    특정 타입으로 제한된 wildcard type 이다. 예를들어 List<? extends Number> 와 같은 형식이다.

     

    Type Erasure

    Oracle 공식 문서에는 Type Erasure에 대해 다음과 같이 설명 되어있다.

    Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.

    Insert type casts if necessary to preserve type safety.

    Generate bridge methods to preserve polymorphism in extended generic types.

    Unbounded wildcard type

    // Before
    public class Node<T> {
    
        private T data;
        private Node<T> next;
    
        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
    
        public T getData() { return data; }
        // ...
    }
    
    // After
    public class Node {
    
        private Object data;
        private Node next;
    
        public Node(Object data, Node next) {
            this.data = data;
            this.next = next;
        }
    
        public Object getData() { return data; }
        // ...
    }

    Unbounded type일 경우 커마일 단계에서 모두 Object로 변환해 준다.

     

    Bounded wildcard type

    // Before
    public class Node<T extends Comparable<T>> {
    
        private T data;
        private Node<T> next;
    
        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }
    
        public T getData() { return data; }
        // ...
    }
    
    // After
    public class Node {
    
        private Comparable data;
        private Node next;
    
        public Node(Comparable data, Node next) {
            this.data = data;
            this.next = next;
        }
    
        public Comparable getData() { return data; }
        // ...
    }

    bounded type일 경우 자신의 bound인 comparable로 변환해 준다.

     

    Bridge Method

    위 Type Erasure 관해 설명할때 Oracle 공식 문서에 다음과 같은 설명도 있다. 

    Generate bridge methods to preserve polymorphism in extended generic types

    다형성을 유지하기 위해 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);
        }
    }

    위와 같은 코드를 컴파일하면 Type Erasure에 의해 다음과 같이 변환된다.

     

    public class Node {
    
        public Object data;
    
        public Node(Object data) { this.data = data; }
    
        public void setData(Object data) {
            System.out.println("Node.setData");
            this.data = data;
        }
    }
    
    public class MyNode extends Node {
    
        public MyNode(Integer data) { super(data); }
    
        public void setData(Integer data) {
            System.out.println("MyNode.setData");
            super.setData(data);
        }
    }

    위에서 발생하는 문제점은, Node를 상속받은 MyNode에서 SetData를 Override해서 사용하려하지만 실제로 컴파일할때 Node의 SetData는 Unbounded type으로 Object로 바뀌기때문에 Node의 setData와 MyNode의 SetData가 다른 함수가 되어 Override가 되지 않는다는것이다. 이를 해결하기 위해 compiler가 bridge method를 다음과 같이 생성하여 문제를 해결한다.

    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);
        }
    
        // ...
    }

    Object를 받는 bridte method setData를 생성해주고 내부적으로 형 변환을 해서 사용자가 override 하려한 setData를 호출해준다.

    'JAVA' 카테고리의 다른 글

    Java mutable 과 immutable  (1) 2023.10.08
    Java와 일급 함수  (0) 2023.09.18
    JVM Memory Area  (0) 2023.09.08
    Java 버전 별 ArrayList add 동작  (0) 2023.09.06
    Java Thread Safety 한 개발  (0) 2023.08.29
Designed by Tistory.