- 비검사 경고는 중요하니 무시하지 말자.
- 모든 비검사 경고는 런타임에
ClassCastException
을 일으킬 수 있는 잠재적 가능성을 뜻하니 최선을 다해 제거해야 한다. - 다행히도 대부분의 비검사 경고는 쉽게 제거할 수 있다.
- 하지만 제거하기 어려운 경고도 있는데 이는 어떻게 처리하는지 이번 장에서 함께 살펴보자
- 먼저 쉬운 비검사 경고를 제거하는 과정을 예시로 살펴보자.
Set<Integer> numbers = new HashSet();
-
위의 코드는 컴파일러가 경고하는 코드
-
컴파일러는 무엇이 잘못됐는지 친절하게 설명해준다.
-
위 코드
Set<Integer> numbers = new HashSet();
에서 경고가 발생하는 이유는HashSet
생성자를 사용할 때 제네릭 타입을 명시하지 않았기 때문 -
제네릭 타입을 명시하지 않으면 'raw type'을 사용하는 것이 되고, 이는 제네릭이 도입된 Java 5 이후로 권장되지 않는 방식이기 때문에 경고했던 것.
-
따라서 코드를 다음과 같이 수정하면 경고가 사라진다.
Set<Integer> numbers = new HashSet<Integer>();
- Java 7 이상을 사용하는 경우, 다이아몬드 연산자(
<>
)를 사용하여 타입 파라미터를 생략할 수도 있음 - 이렇게 하면 컴파일러가 문맥을 바탕으로 적절한 타입 인수를 추론할 수 있다
Set<Integer> numbers = new HashSet<>();
- 이제 경고는 제거되었다.
- 경고가 제거된 코드는
ClassCastException
이 방지되는 장점을 가진다.
- 다음과 같은 경우도 있을 수 있겠다.
경고를 제거할 수는 없지만 타입 안전하다고 확신할 수 있다.
- 이럴 경우는 안전함을 확신할 수 있는 경우이기에 경고를 숨기는 것이 해결이 될 수 있다.
- 단 타입 안전함을 검증하지 않은 채 경고를 숨기면 스스로에게 잘못된 보안 인식을 심어주는 꼴이다.
- 그러한 코드는 경고 없이 컴파일되겠지만, 런타임에는 여전히
ClassCastException
을 던질 수 있기 때문이다. - 또 반대로, 안전하다고 검증된 비검사 경고를 그대로 두면, 진짜 문제를 알리는 새로운 경고가 나와도 눈치채지 못할 수 있다.
- 제거하지 않은 수많은 거짓 경고 속에 중요한 경고가 파묻힐 수 있기 때문이다.
- 경고는 필요한 맥락에 적절히 등장하도록 해야 한다는 얘기
@SuppressWarnings
애너테이션을 통해서 경고를 숨길 수 있다.- 이 애너테이션은 개별 지역변수 선언부터 클래스 전체까지 어떤 선언에도 달 수 있다.
- 하지만
@SuppressWarnings
애너테이션은 항상 가능한 좁은 범위에 적용하자. - 클래스 전체 등, 더 넓게 적용되면 자칫 심각한 경고를 놓칠수도 있기 때문이다.
- 그리고
@SuppressWarnings
애너테이션은 지역 변수든 클래스든 선언에만 달 수 있기 때문에 return문 등에 애너테이션을 달고 싶은 경우 다른 방법을 사용하자 - 가장 쉬운 방법은 메서드의 선언 부로 애너테이션을 옮기는 것이겠지만 가능한 좁은 범위에 적용하기 위해서 지역변수를 추출할 수 있다.
public <T> T[] toArray(T[] a) {
if(a.length < size) {
@SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass());
return result;
}
...
...
}
- 위의 코드는 깔끔하게 컴파일되고 비검사 경고를 숨기는 범위도 최소로 좁혔다.
- 다만 또하나 명심하자
@SuppressWarnings
애너테이션을 사용할 때면 그 경고를 무시해도 안전한 이유를 항상 주석으로 남기자.- 다른 사람이 코드를 이해하는 데 도움이 되며, 더 중요하게는, 다른 사람이 코드를 잘못 수정하여 타입 안전성을 잃는 상황을 줄여주기 때문이다.
public <T> T[] toArray(T[] a) {
if(a.length < size) {
// 생성한 배열과 매개변수로 받은 배열의 타입이 모두 T[]로 같으므로 올바른 형변환
@SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass());
return result;
}
...
...
}