[item 27] 비검사 경고를 제거하라

김동훈·2023년 7월 22일
1

Effective-java

목록 보기
10/14
post-thumbnail

우선 비검사 경고는 간략하게 다음을 말한다.

Runtime에 발생하고, 명시적인 예외처리를 하지 않는 Exception이다.

이번 item에 나온 비검사 경고는 ClassCastException이다. 이 예외는 당연히 RuntimeException을 상속받고 있다.

public class ClassCastException extends RuntimeException {
    private static final long serialVersionUID = -9223365651070458532L;

    /**
     * Constructs a <code>ClassCastException</code> with no detail message.
     */
    public ClassCastException() {
        super();
    }

    /**
     * Constructs a <code>ClassCastException</code> with the specified
     * detail message.
     *
     * @param   s   the detail message.
     */
    public ClassCastException(String s) {
        super(s);
    }
}

unchecked conversion

Compile-time warning

타입확인없이 로 타입으로 선언할 때 발생

Set<Lark> exaltation = new HashSet();

위 코드는 unchecked conversion 라는 비검사 경고가 발생한다. 이러한 선언은 허용된다. 그 이유는 제네릭이 지원되지 않았던, 이전 Java버전과의 호환성을 위해서이다.
이 경고를 해결하기 위해서는, 올바른 타입 매개변수를 명시하거나, 다이아몬드 연산자(<>)를 사용하면된다.

// 방법 1
Set<Lark> exaltation = new HashSet<Lark>();
// 방법 2
Set<Lark> exaltation = new HashSet<>();

제어할 수 없는 비검사 경고

그런데, 만약 이러한 비검사 경고를 우리가 제어할 수 없을때도 있을것이다. 예를 들어, 그럴일은 없겠지만 외부라이브러리의 잘못된 메소드를 사용하는 경우이다. 이 메소드에서 비검사 경고가 발생할 코드가 있다면, 즉 다음과 같은 메소드이다.

//외부 라이브러리 메소드.
public static List getRawList() {
        List result = new ArrayList();
        result.add("I am the 1st String.");
        result.add("I am the 2nd String.");
        result.add("I am the 3rd String.");
        return result;
    }
    
// 이 외부 라이브러리 메소드를 사용하는 클라이언트 코드.
List<String> rawList = getRawList();

이 메소드는 [unchecked] unchecked call to add(E) as a member of the raw type List라는 비검사경고가 발생하고, 반환타입은 List 로 타입이다. 따라서 이를 사용하는 클라이언트에서 List<String> rawList = getRawList();와 같이 사용한다면, 자동 형변환되는 과정에서 unchecked conversion 경고가 발생할 것이다. 이러한 상황이 위에서 말한 제어할 수 없는 상황이다.

그럼 이럴 때엔 어떻게 비검사 경고를 제거할까??

@SuppressWarnings(“unchecked”)

이런경우 @SuppressWarnings(“unchecked”) 어노테이션을 통해 해결할 수 있다. 이때 주의해야 할 점은, 반드시 우리가 type safe함을 보장하는 경우에만 사용가능하다라는 것이다. 위 외부라이브러리 메소드라면 내부에서 String만을 List에 담고있기때문에 어노테이션으로 경고를 숨겨주어도 괜찮다.

만약, 타입 안전함을 검증하지 않고 사용해버리면 어떻게 될까??
예를 들어, 다음과 같이 수정된 외부 라이브러리 메소드를 사용한다고 하자.

// 위험한 외부 라이브러리 메소드.
public static List getRawListWithMixedTypes() {
        List result = new ArrayList();
        result.add("I am the 1st String.");
        result.add("I am the 2nd String.");
        result.add("I am the 3rd String.");
        result.add(new Date());
        return result;
    }
    
// 이 외부 라이브러리 메소드를 사용하는 클라이언트 코드.
@SuppressWarnings("unchecked")
List<String> rawListWithMixedTypes = getRawListWithMixedTypes();
String s = rawListWithMixedTypes.get(3); // ClassCastException 발생.

실제 클라이언트에서 형변환시에는 @SuppresssWarnings 어노테이션을 사용했으므로, 비검사 경고는 보이지 않는다. 그리고 반환받은 List의 데이터를 가져올 때, 그 타입은 당연히 String타입으로 받게 된다. 하지만 이 rawListWithMixedTypes에는 String + Date조합의 데이터이다. 따라서, 이 Date객체를 String으로 받으면서 ClassCastException예외가 발생하는데 이는 컴파일시점이 아닌 런타임시점에 알 수 있다.

따라서, @SuppressWarnings(“unchecked”) 를 사용할 땐 Type Safe를 보장해야한다. 이와 함께 경고를 무시해도 안전한 이유를 항상 주석으로 남기자.

결론

모든 비검사 경고는 런타임에 ClassCastException의 발생가능성을 의미하니, 최선을 다해 제거하라.

제거할 방법이 없다면, 그 코드가 안전함을 증명하고 최소한의 범위로 @SuppressWarnings("unchecked") 어노테이션으로 숨겨라.

그리고 그 근거를 주석으로 남겨라.

참고


effective-java스터디에서 공유하고 있는 전체 item에 대한 정리글

profile
董訓은 영어로 mentor

0개의 댓글