지네릭 타입의 형변환과 제거

2022. 5. 16. 15:23·자바/지네릭스

1. 지네릭 타입과 원시 타입의 형변환

- 아래의 코드처럼 지네릭 타입과 원시 타입의 형변환은 항상 가능하지만, 경고가 발생한다.

Box box = null;
Box<Object> objBox = null;

box = (Box) objBox;         // 지네릭 타입 -> 원시 타입 (경고 발생)
objBox = (Box<Object>) box; // 원시 타입 -> 지네릭 타입 (경고 발생)

 

 

 

- 단, 아래의 코드처럼 대입된 타입이 다른 지네릭 타입 간에는 형변환이 불가능하다.

Box<Object> objBox = null;
Box<String> strBox = null;

objBox = (Box<Object>) strBox;
// 에러 발생

strBox = (Box<String>) objBox;     
// 에러 발생

 

 

- 그러므로, 아래의 코드처럼 <Object> 지네릭으로는 다른 지네릭으로 형변환할 수 없다.

Box<Object> objBox = new Box<String>();
// 에러 발생. 형변환 불가능

 

 

- 그러나, 아래의 코드처럼 <? extends Object>를 사용하여 <String> 지네릭으로 변경할 수 있다.

Box <? extends Object> wBox = new Box<String>();
// 형변환 가능.

 

 

- 만약 아래의 코드처럼 반대로 형변환을 한다면, 확인되지 않는 형변환이라는 경고가 발생한다.

FruitBox <? extends Fruit> box = null;
FruitBox <Apple> appleBox = (FruitBox<Apple>) box;
// 형변환 OK. but 경고 발생

 

 

- 아래의 코드는 java.util.Optional 클래스의 실제 소스이다.

public final class Optional<T>{
    private static final Optional<?> EMPTY = new Optional<>();
    private final T value;
    ...
    
    public static<T> Optional<T> empty(){
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    
        ...
}

 

( 상수 EMPTY 변수는 아래의 코드와 동일하다. 접근 제어자는 생략했다. )

( (1) Optional<?> EMPTY = new Optional<Object>(); )

( (2) Optional<? extends Object> EMPTY = new Optional<>(); )

( (3) Optional<? extends Object> EMPTY = new Optional<Object>(); )

 

( 사실 <?>는 <? extends Object>를 생략하였고, (1)의 <> 안에 생략된 타입은 "?"가 아닌 "Object"이다. )

 

( 단, class Box<T extends Fruit>일 경우, Box<?> b = new Box<>;는 Box<?> b = new Box<Fruit>; 이다. )

 

( 그러므로, Optional<?> Empty = new Optional<?>(); 는 에러를 발생하게 된다. )

( 그 이유는 미확인 타입의 객체로 간주하기 때문이다. )

 

 

( 또한 상수 EMPTY 타입을 Optional<Object>가 아닌 Optional<?>로 한 이유는 아래의 코드처럼 Optional<T>로

형변환이 가능하기 때문이다. ) 

Optional<?>      wopt = new Optional<Object>();
Optional<Object> oopt = new Optional<Object>();

Option<String> sopt = (Optional<String>) wopt;
// 컴파일 OK. 형변환 가능

Option<String> sopt = (Optional<String>) oopt;
// 컴파일 에러. 형변환 불가능

 

 

( 그러므로, Optional<Object>를 Optional<String>으로 직접 형변환하는 것은 불가능하지만, 와일드 카드가

포함된 지네릭 타입으로 형변환하면 가능하다. )

( Optional 클래스의 empty() 메서드만 봐도 형변환을 수행하고 있다. )

Optional<T> t = (Optional<T>) EMPTY;
// Optional<?> -> Optional<T>

 

 

( 대신 확인되지 않는 타입으로의 형변환이라는 경고가 발생한다. )

( 아래의 코드를 통해 이해하자. )

Optional<Object> -> Optional<T>
// 형변환 불가능

Optional<Object> -> Optional<?> -> Optional<T>
// 형변환 가능. 경고 발생

 

 

( 마지막으로, 와일드 카드를 사용한 지네릭 타입끼리도 아래의 코드처럼 형변환이 가능하다. )

FruitBox<? extends Object> objBox = null;
FruitBox<? extends String> strBox = null;

strBox = (FruitBox<? extends String>) objBox;
objBox = (FruitBox<? extends Object>) strBox;
// 둘 다 형변환 가능 but 미확정 타입으로 경고

 

 

 

 

2. 지네릭 타입의 제거

- 컴파일러는 지네릭 타입을 이용해서 소스파일을 체크하고, 필요한 곳에 형변환을 넣어준다. 그리고 지네릭 타입을

제거한다. 그러므로, (*.class)에는 지네릭 타입에 대한 정보가 없는 것이다. 

 

- 이렇게 하는 주된 이유는 지네릭이 도입되기 이전의 소스 코드와의 호환성을 유지하기 위해서이다.

 

 

- 아래의 순서는 지네릭 타입을 제거하는 컴파일러의 과정을 보여준다.

 

(1) 지네릭 타입의 경계(Bound)를 제거한다.

적용 전 적용 후
class Box<T extends Fruit>{
   void add(T t) {
          ...
   }
}
class Box{
   void add(Fruit t) {
          ...
   }
}

( <T extends Fruit>라면 T는 Fruit로 치환한다. )

( 또한 <T>인 경우는 T가 Object로 치환된다. )

 

 

 

(2) 지네릭 타입을 제거한 후에 타입이 일치하지 않으면, 형변환을 추가한다.

적용 전 적용 후
T get(int i){
     return list.get(i);
}
Fruit get(int i){
      return (Fruit) list.get(i);
}

( 여기서는 컴파일러가 T를 치환했을 뿐만 아니라 형변환을 적용했다. )

 

 

( 만약 와일드 카드가 포함된 경우에는 아래와 같이 적절한 타입으로의 형변환이 추가된다. )

 

< 적용 전 >

class Juicer{
    static Juice makeJuice(FruitBox<? extends Fruit> box) {
         String tmp = "";
         for(Fruit f : box.getList()) tmp += f + " ";
         return new Juice(tmp);
    }
}

 

< 적용 후 >

class Juicer{
    static Juice makeJuice(FruitBox  box) {
         String tmp = "";
         Iterator it = box.getList().iterator();
         while(it.hasNext()){
               tmp += (Fruit) it.next() + " ";
         }
         return new Juice(tmp);
    }
}
저작자표시 (새창열림)

'자바 > 지네릭스' 카테고리의 다른 글

지네릭 메서드  (0) 2022.05.13
와일드 카드  (0) 2022.05.12
지네릭스의 전반적 개념(2)  (0) 2022.05.11
지네릭스의 전반적 개념(1)  (0) 2022.05.11
'자바/지네릭스' 카테고리의 다른 글
  • 지네릭 메서드
  • 와일드 카드
  • 지네릭스의 전반적 개념(2)
  • 지네릭스의 전반적 개념(1)
백_곰
백_곰
  • 백_곰
    친절한 코딩
    백_곰
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 알고리즘 (with JAVA)
        • 기본 알고리즘
        • 완전 탐색
        • 분할 정복 알고리즘
        • 동적 계획법
        • 탐욕법
        • 코딩 테스트 기출 문제
        • 코드트리 조별과제
      • 백준 (with JAVA)
        • 완전 탐색
        • 분할 정복
        • 그 외
      • 자바
        • 개발 환경 구축하기
        • 팁
        • 기본적인 개념
        • 컬렉션 프레임워크
        • 프로세스와 쓰레드
        • 지네릭스
        • 람다식
        • 스트림
        • 입출력 IO
        • 네트워킹
        • 열거형(enums)
        • java.lang 패키지
        • java.time 패키지
        • 유용한 클래스들
        • 형식화 클래스들
      • 안드로이드 with 자바
        • 응용 문제들
        • 자잘한 문제들
        • 오류 보고서
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      중간연산
      serializable
      자바 개념
      InputStream
      안드로이드 스튜디오
      알고스팟
      Collections Framework
      선택 정렬
      스트림
      제자리 정렬
      코딩트리조별과제
      소켓 프로그래밍
      outputstream
      TCP 소켓 프로그래밍
      안정 정렬
      문자 기반 스트림
      java.lang패키지
      역직렬화
      map()
      코딩테스트
      람다식
      snail
      ServerSocket
      유용한 클래스
      java.time 패키지
      file
      Arrays
      코드트리
      다형성
      불안정 정렬
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    백_곰
    지네릭 타입의 형변환과 제거
    상단으로

    티스토리툴바