Remove Unchecked Warning!

jiho·2021년 5월 27일
0

EffectiveJava

목록 보기
10/12

Effective Java Item 27 비검사 경고를 제거하라

에 대한 내용을 정리하겠습니다.

소프트웨어 개발을 하다보면 어떤 언어이든 프레임워크든 항상 마주치는 warning들이 있습니다. 너무 많은 warning을 다양한 레벨에서 쏟아내서 보기 힘들 정도도 있습니다. 한번은 warning어떻게 바라봐야하고 어떻게 처리할지 고민해볼만합니다. Generic과 관련된 warning 위주로 정리해보겠습니다.

Generic을 사용하기 시작하면 수 많은 컴파일러 경고를 보게 될 것입니다.

  • Unchecked Casting 경고
  • Unchecked Method call 경고
  • Unchecked 매개변수화 가변인수 타입 경고
  • Unchecked 변환 경고
    ...

대부분의 Unchecked warning은 쉽게 제거할 수 있습니다.

Set<String> example = new HashSet();

unchecked 경고가 컴파일 단계에 발생합니다.

위 경고는 간단히 RawType의 HashSet을 사용한 했다는 것과 Unchecked assignment 이기 때문에 new HashSet<String>()처럼 명시적으로 타입을 지정해주거나 자바 7부터 지원하는 <>다이아몬드 연산자만으로 해결할 수 있습니다.

Set<String>example = new HashSet<>();

그러면 컴파일러가 실제 타입 매개변수를 추론해줍니다.

이런 간단한 경고 이외에도 제거하기 어려운 경고도 있습니다.
할 수 있는한 비검사 경고를 제거하는 것이 좋습니다. 모두 제거한다면 그 코드는 타입 안정성을 보장할 수 있습니다.

하지만 경고를 제거할 수는 없지만 타입이 안전하다고 확실 할 수 있다면 @SuppressWarnings("unchecked") annotation을 달아 경고를 숨길 수 있습니다.

단, 타입 안정성을 제대로 검증하지 않은채 경고를 숨기면 제 무덤을 파는 꼴이 됩니다. 코드는 경고 없이 컴파일 되지만 런타임에는 여전히 ClassCastException을 던질 수 있습니다.(디버깅이 어려워짐)

하지만 안전하다고 검증된 unchecked warning을 그대로 둔다면, 진짜 문제를 알리는 warning들이 나와도 warning들이 너무 많아서 인지하기 어려울 수 있습니다.

@SuppressWarnings 사용해서 검증된 warning을 없애자.

SuppressWarnings annotation은 개별 지역변수부터 클래스 전체까지 어떤 선언에도 달수 있다. 하지만 인지하지 못한 Warning들이 무시될 수도 있기 때문에 항상 가능한 한 좁은 범위에 적용해야합니다. 자칫해서 심각한 경고를 놓칠 수 있으니 절대로 클래스 전체에 적용해서는 안됩니다.

한줄 이상의 생성자나 메서드에 @SuppressWarnings이 달려있다면 지역변수 선언으로 옮기는 것이 좋은 습관입니다. 없던 지역변수를 새로 선언할 수고를 들일 수도 있지만 그만한 값어치가 있습니다.

간단한 예를 위해 ArrayList에서 가져온 toArray를 살펴보겠습니다.

public <T> T[] toArray(T[] a) {
    if(a.length < size){
    	return (T[])Arrays.copyOf(elements, size, a.getClass());
    }
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size){
        a[size] = null;
    }
    return a;
}

ArrayList를 컴파일하면

return (T[])Arrays.copyOf(elements, size, a.getClass());

elements가 Object[] 타입이기 때문에 unchecked cast warning이 발생합니다.
하지만 같은 타입임을 보장할 수 있기 때문에 @SuppressWarnings를 이용해서 경고를 제거해주는 것이 좋습니다. 위 코드에서 return 문에 어노테이션을 달수가 없기에 해당 method 전체에 어노테이션을 달 수 있습니다.

하지만 또 그렇게하면 필요이상으로 경고들을 무시할 수 있기 때문에 아래와 같이 따로 지역 변수를 선언해서 경고를 제거해주는 방법이 좋습니다.

@SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass());
return result

위 처럼 경고를 제거할 때는 최소한의 범위로 좁히는 것이 좋습니다.(이유는 Exception의 try...catch의 범위를 좁히는 내용과 유사합니다.)

추가로 경고를 무시했으면 무시해도 안전한 이유를 주석으로 남겨야합니다. 그래야지 다른사람이 코드를 보고 어노테이션을 제거하는 일이 발생하지 않습니다.

정리

Unchecked Exception은 중요하니 무시하면 안됩니다. 모든 비검사 경고는 런타임에 ClassCastException를 일으킬 수 있는 잠재적 가능성을 뜻하니 최선을 다해 제거해야합니다.

어떻게 해도 경고를 없애 방법을 찾지못하면

  • 타입 안전함을 증명하고
  • 최소한의 범위로 @SuppressWarnings를 적용해서 경고를 지우고
  • 주석으로 다음 경고를 숨기기로한 근거를 주석으로 남겨야합니다.
profile
Scratch, Under the hood, Initial version analysis

0개의 댓글