JAVA

제네릭 타입의 컴파일 영향

whyWhale 2021. 4. 10.

사전학습 : Generic , 배열과 제네릭의 차이


 

 

Generic

  • 재너릭이라는 용어는 포괄적인 이라는 뜻으로, 클래스 내부에 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다.
  • 다이아몬드 표기법을 사용한다. (ex. <>)

특징

  • 같은 구조를 갖는 코드의 중복을 줄일 수 있다.
  • 타입 안정성이 보장된다.(컴파일시 타입 체크가 가능하다)
    • 타입 제한도 가능(extends, implement)
  • 참조 타입(Wrapper Class)만 사용이 가능하다.
  • 메소드 적용이 가능하다.

 

 

재너릭과 배열의 차이

 

1)재너릭은 불 공변, 배열은 공변 이다.

 

  • 공변 = "자기 자신과 자식" 객체로 타입 변환을 허용해주는 것.
  • 불 공변 ="자기 자신" 을 제외한 타입 변환이 허용되지 않는 것. __ 제너릭의 타입 안정성이 보장되는 원리

ex) 불공변의 예

         void foo(ArrayList<Object> arr=new ArrayList<>()) {

                            }

         public static void main(STring[] args){

               ArrayList<Integer> list=new new ArrayList<>();

                            list.add(1);

                            ist.add(2);

                            list.add(3);

                            foo(list); // compile error

                  }

ex) 공변의 예

           Object[] arr=new int[1];

 

 

2)배열은 구체화되고 제네릭은 비 구체화가 된다.

 

  • 구체화 타입 : 자신의 타입 정보를 런타임에도 알고 있는 것.
  • 비 구체화 타입 : 런타임에는 소거(erasure)가 되기 때문에 컴파일 타임보다 정보를 적게 가지는 것.

※ 제네릭은 컴파일 타임에 타입 체크를 한 후에 런타임에는 타입을 지우는 방법을 사용하고 있다.

 

 

 

 

재너릭 컴파일의 영향


 

 

Generic Type erasure란?

 

원소 타입을 컴파일 타입에만 검사하고 런타임에는 해당 타입 정보를 알수 없는 것.

즉, 컴파일 타임에만 타입 제약 조건을 정의하고, 런타임에는 타입을 제거한다는 뜻이다.

 

 

 

Java Compiler 타입 소거

 

  • unbounded Type(<?>,<T> ) 는 Object로 변환.
  • bound Type( <E extends Comparable>) 의 경우 Comparable로 변환.
  • 제네릭 타입을 사용할 수 있는 일반 클래스, 인터페이스, 메소드에만 소거 규칙 사용.
  • 타입 안정성을 위해 type casting을 넣는다.
  • 확장된 제네릭 타입에서 다형성을 보존하기 위해 bridge method를 생성.

 

 

1. unbounded Type

// 타입 소거 전(컴파일 할 때)
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());
    }
}

 

 

2. bound Type

// 타입 소거 전 ( 컴파일 할때 )
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;
    }
}

3. bridge method

 

※ 자바 컴파일러가 제네릭의 타입 안정성을 좀 더 보장하고자 브릿지 메소드를 만들어 낸다.

 

// 소거 전
public class MyComparator implements Comparator<Integer> {
   public int compare(Integer a, Integer b) {
      //
   }
}


// 소거 후
public class MyComparator implements Comparator {
   public int compare(Integer a, Integer b) {
      //
   }
}

 

 그리고 Obejct로 바뀌고 나서 컴파일러는 메소드 시그니처 사이에 불일치를 없애기 위해서 컴파일러는 런타임에 해당 제네릭 타입의 타입소거를 위한 bridge method를 만들어 준다.

// 런타임 떄
public class MyComparator implements Comparator<Integer> {
   public int compare(Integer a, Integer b) {
      //
   }

   // "bridge method" == > 시그니처 사이 불일치를 없애기 위해.
   public int compare(Object a, Object b) {
      return compare((Integer)a, (Integer)b);
   }
}

 

그러므로 매개변수 타입 Integer 타입이 보장되게 된다.

 

 

'JAVA' 카테고리의 다른 글

Garbage Collector  (0) 2021.04.25
Reflection  (0) 2021.04.16
자바의 compile 과정  (0) 2021.04.04
JVM 구조와 버전에 따른 변경  (0) 2021.03.28
Wrapper Class 와Primitive Type and First Class Collection  (0) 2021.03.21

댓글