1. 개요

때때로 Java 소스 파일을 컴파일 할 때 Java 컴파일러에 의해 인쇄 된 " unchecked cast "경고 메시지가 표시됩니다.

이 사용방법에서는 경고 메시지를 자세히 살펴 보겠습니다. 이 경고의 의미, 경고를받는 이유 및 문제 해결 방법에 대해 설명합니다.

일부 Java 컴파일러는 기본적으로 확인되지 않은 경고를 표시하지 않습니다.

이 " unchecked cast "경고를 살펴보기 전에 "unchecked"경고를 인쇄하는 컴파일러 옵션을 활성화 했는지 확인하십시오 .

2.“ 체크되지 않은 캐스트 ”경고 는 무엇을 의미합니까?

" unchecked cast "는 컴파일 타임 경고 입니다. 간단히 말해,  유형 검사없이 원시 유형을 매개 변수화 된 유형으로 캐스팅 할 때이 경고가 표시됩니다 .

예를 들어 간단하게 설명 할 수 있습니다. 원시 유형 Map 을 반환하는 간단한 메서드가 있다고 가정 해 보겠습니다 .

public class UncheckedCast {
    public static Map getRawMap() {
        Map rawMap = new HashMap();
        rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
        rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
        rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
        return rawMap;
    }
...
}

이제 위의 메서드를 호출하고 결과를 Map <String, LocalDate>로 캐스팅하는 테스트 메서드를 만들어 보겠습니다 .

@Test
public void givenRawMap_whenCastToTypedMap_shouldHaveCompilerWarning() {
    Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMap();
    Assert.assertEquals(3, castFromRawMap.size());
    Assert.assertEquals(castFromRawMap.get("date 2"), LocalDate.of(1992, Month.AUGUST, 8));
}

컴파일러는 제네릭을 지원하지 않는 이전 Java 버전과의 역 호환성을 유지하기 위해이 캐스트를 허용해야합니다.

그러나 Java 소스를 컴파일하면 컴파일러가 경고 메시지를 인쇄합니다. 다음으로 Maven을 사용하여 단위 테스트를 컴파일하고 실행 해 보겠습니다.

$ mvn clean test
...
[WARNING] .../src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java:[14,97] unchecked cast
  required: java.util.Map<java.lang.String,java.time.LocalDate>
  found:    java.util.Map
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
...
[INFO] Results:
[INFO] 
[INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...

Maven 출력에서 ​​알 수 있듯이 경고를 성공적으로 재현했습니다.

다른 한편으로 우리의 테스트는“ unchecked cast ”컴파일러 경고를 보더라도 아무런 문제없이 작동합니다 .

컴파일러가 이유없이 경고하지 않을 것임을 알고 있습니다. 이 경고를 볼 때 잠재적 인 문제가있을 것입니다.

알아 내자.

3. Java 컴파일러가 경고하는 이유는 무엇입니까?

" unchecked cast "경고 가 표시되지만 테스트 방법은 이전 섹션에서 잘 작동합니다 . 원시 유형 Map 을  Map <String, LocalDate> 로 캐스팅 할 때 원시 Map 에는 <String, LocalDate> 항목 만 포함 되기 때문 입니다. 즉, typecasting은 안전합니다.

잠재적 인 문제를 분석하기 위해 원시 유형 Map에 항목을 하나 더 추가 하여 getRawMap () 메서드를 약간 변경해 보겠습니다  .

public static Map getRawMapWithMixedTypes() {
    Map rawMap = new HashMap();
    rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
    rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
    rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
    rawMap.put("date 4", new Date());
    return rawMap;
}

이번에 는 위의 방법에서 <String, Date> 유형 의 새 항목을 맵에 추가했습니다 .

이제 getRawMapWithMixedTypes () 메서드 를 호출하는 새 테스트 메서드를 작성해 보겠습니다 .

@Test(expected = ClassCastException.class)
public void givenMixTypedRawMap_whenCastToTypedMap_shouldThrowClassCastException() {
    Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes();
    Assert.assertEquals(4, castFromRawMap.size());
    Assert.assertTrue(castFromRawMap.get("date 4").isAfter(castFromRawMap.get("date 3")));
}

테스트를 컴파일하고 실행하면 " unchecked cast "경고 메시지가 다시 인쇄됩니다. 또한 테스트를 통과합니다.

그러나 우리 테스트에는 expected = ClassCastException.class 인수가 있으므로 테스트 메서드가 ClassCastException 을 throw 했음을 의미 합니다 .

자세히 살펴보면 경고 메시지가이 줄을 가리 키더라도 원시 유형 MapMap <String, LocalDate> 로 캐스팅하는 줄 에서 ClassCastException 이 발생하지 않습니다대신, 키에 의해 잘못된 유형의 데이터를 가져올 때 예외가 발생합니다 . castFromRawMap.get ( "date 4"). 

잘못된 유형의 데이터를 포함하는 원시 유형 컬렉션을 매개 변수화 된 유형 컬렉션으로 캐스팅하면 잘못된 유형 의 데이터를로드 할 때까지 ClassCastException 이 발생하지 않습니다 .

때로는 예외가 너무 늦게 발생할 수 있습니다.

예를 들어 메소드를 호출하여 많은 항목 이있는 원시 유형 Map얻은  다음 매개 변수화 된 유형 을 사용하여 Map 으로 캐스트합니다 .

(Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes()

Map의 각 항목에 대해  LocalDate 객체를 원격 API 로 보내야합니다 . ClassCastException이 발생할 때까지 이미 많은 API 호출이 수행되었을 가능성이 큽니다. 요구 사항에 따라 일부 추가 복원 또는 데이터 정리 프로세스가 관련 될 수 있습니다.

잘못된 유형의 항목 상황을 처리하는 방법을 결정할 수 있도록 더 일찍 예외를 얻을 수 있다면 좋을 것입니다.

unchecked cast ”경고 의 잠재적 인 문제를 이해 했으므로 문제 를 해결하기 위해 무엇을 할 수 있는지 살펴 보겠습니다.

4. 경고를 어떻게해야합니까?

4.1. 원시 유형 사용 피하기

제네릭은 Java 5부터 도입되었습니다. Java 환경이 제네릭을 지원하는 경우 원시 유형을 사용하지 않아야합니다. 원시 유형을 사용하면 제네릭의 모든 안전성과 표현력 이점을 잃을 수 있기 때문 입니다.

또한 레거시 코드를 검색하고 원시 유형 사용을 제네릭으로 리팩터링해야합니다.

그러나 때때로 우리는 오래된 라이브러리와 함께 작업해야합니다. 이러한 오래된 외부 라이브러리의 메서드는 원시 형식 컬렉션을 반환 할 수 있습니다.

이러한 메서드를 호출하고 매개 변수가있는 유형으로 캐스팅하면 " unchecked cast "컴파일러 경고 가 생성됩니다 . 그러나 우리는 외부 라이브러리를 제어 할 수 없습니다.

다음으로이 케이스를 처리하는 방법을 살펴 보겠습니다.

4.2. " unchecked "경고 표시 안 함

" unchecked cast "경고를 제거 할 수없고 경고를 유발하는 코드가 유형 안전하다고 확신하는 경우 SuppressWarnings ( "unchecked") 어노테이션을 사용하여 경고 억제 할 수 있습니다 .

@ SuppressWarning ( "unchecked") 어노테이션 을 사용할 때는 항상 가능한 가장 작은 범위에 배치해야합니다.

ArrayList 클래스 remove () 메서드를 예로 들어 보겠습니다.

public E remove(int index) {
    Objects.checkIndex(index, size);
    final Object[] es = elementData;
                                                              
    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    fastRemove(es, index);
                                                              
    return oldValue;
}

4.3. 원시 유형 콜렉션을 사용하기 전에 유형 안전성 검사 수행

우리가 배운 것처럼 @SuppressWarning ( "unchecked") 어노테이션은 캐스트가 typesafe인지 실제로 확인하지 않고 경고 메시지를 억제합니다.

우리가 원시 형을 주조하는 형태 보증 확실하지 경우 경우, 우리는해야 우리가 정말 데이터를 사용하기 전에 유형을 확인 그래서 우리가 얻을 수  ClassCastException이 이전 .

5. 결론

이 기사에서는 " unchecked cast "컴파일러 경고가 의미 하는 바를 배웠습니다 .

또한이 경고의 원인과 잠재적 인 문제를 해결하는 방법을 설명했습니다.

항상 그렇듯이이 글의 코드는 모두 GitHub에서 사용할 수  있습니다 .