일상기록
Java - 제네릭 타입의 형변환 본문
제네릭 타입의 형변환
제네릭 타입과 원시 타입(raw type)간의 형변환이 가능할까? 아래 코드를 보면
Box box = null;
Box<Object> objBox = null;
box = (Box)objBox; // OK 제네릭 타입 → 원시 타입(안쓰는게 바람직) 경고발생
objBox = (Box<Object>)box; // OK 원시 타입(안쓰는게 바람직) → 제네릭 타입 경고발생
코드를 보면 알 수 있듯이, 제네릭 탕비과 논제네릭(non-generic) 타입간의 형변환은 항상 가능하다. 다만 경고가 발생할 뿐이다. 그렇다면 대입된 타입이 다른 제네릭 타입 간에 형변환은 가능할까?
Box<String> srtBox = null;
Box<Object> objBox = null;
srtBox = (Box<String>)objBox; // 에러 Box<Object> → Box<String>
objBox = (Box<Object>)srtbox; // 에러 Box<String> → Box<Object>
불가능하다. 대입된 타입이 Object일지라도 말이다. 이 사실은 이미 배웠다. 아래의 코드가 안 된다는 얘기는 Box<String>이 Box<Object>로 형변환될 수 없다는 사실을 간접적으로 알려주는 것이다.
//Box<Object> objBox = (Box<Object>)new Box<String>(); // (Box<Object>) 생략가능
Box<Object> objBox = new Box<String>(); // 에러 형변환 불가능
그렇다면 Box<? extends Object>라면 형변환 가능할까?
Box<? extends Object> wBox = new Box<String>(); // OK
가능하다 다형성이 적용되는 것이다. 반대로의 형변환도 성립하지만 확인되지 않은 형변환이라는 경고가 발생한다.
실제 예시를 보면 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;
}
...
}
static상수 EMPTY에 비어있는 Optional객체를 생성해서 저장했다가 empty()를 호출하면 EMPTY를 형변환해서 반환한다. 먼서 상수를 선언하는 문장을 보면 다음과 같다.
Optional<?> EMPTY = new Optional<>();
→ Optional<? extends Object> EMPTY = new Optional<>();
→ Optional<? extends Object> EMPTY = new Optional<Object>();
<?>는 <? extends Object>를 줄여서 쓴 것이며, <>안에 생략된 타입은 '?'가 아니라 'Object'이다.
Optional<?> EMPTY = new Optional<?>(); // 에러 미확인 타입의 객체는 생성불가
Optional<?> EMPTY = new Optional<Object>(); // OK
Optional<?> EMPTY = new Optional<>(); // OK 위의 문장과 동일
위 문장에서 EMPTY의 타입을 Optional<Object>가 아니 Optional<?>로 한 이유는 Optional<T>로 형변환이 가능하기 때문이다.
Optional<?> wopt = new Optional<Object>();
Optional<Object> oopt = new Optional<Object>();
Optional<String> sopt = (Optional<String>)wopt; // OK 형변환 가능
Optional<String> sopt = (Optional<String>)oopt; // 에러 형변환 불가
empty()의 반환 타입이 Optional<T>이므로 EMPTY를 Optional<T>로 형변환해야 하는데, 위의 코드에서 알 수 있는 것처럼 Optional<Object>는 Optional<T>로 형변환이 불가능 하다.
public static<T> Optional<T> empty() {
Optional<T> t = (Optional<T>) EMPTY; // Optional<?> → Optional<T>
return t;
}
정리하면, Optional<Object>를 Optional<String>으로 직접 형변환하는 것은 불가능하지만, 와일드 카드가 포함된 제네릭 타입으로 형변환하면 가능하다. 대신 확인되지 않은 타입으로의 형변환이라는 경고가 발생한다.
Optional<Object> → Optional<T> // 형변환 불가능
Optional<Object> → Optional<?> → Optional // 형변환 가능, 경고발생
마지막으로 다음과 같이 와일드 카드가 사용된 제네릭 타입 끼리도 다음과 같은 경우에는 형변환 가능하다.
FruitBox<? extend String> srtBox = null;
FruitBox<? extend Object> objBox = null;
srtBox = (FruitBox<? extend String>)objBox; // OK 미확정 타입으로 형변환 경고
objBox = (FruitBox<? extend Object>)srtbox; // OK 미확정 타입으로 형변환 경고
형변환이 가능하긴 하지만, 와일드 카드는 타입이 확정된 타입이 아니므로 컴파일러는 미확정 타임으로 형변환하는 것이라고 경고한다.
'Java' 카테고리의 다른 글
Java - 열거형(Enums) (0) | 2023.04.18 |
---|---|
Java - 제네릭 타입의 제거 (0) | 2023.04.18 |
Java - 제네릭 메소드 (0) | 2023.04.18 |
Java - 와일드 카드 (0) | 2023.04.17 |
Java - 제네릭(Generic) (0) | 2023.04.17 |