개요
제네릭을 사용하면 많은 비검사 경고들을 마주한다. 필자의 경우 이러한 경고들은 IDE의 도움을 받아 수정했으며, 수정 자체에 큰 의의를 두진 않았다. 하지만 이러한 경고를 제거하는 것만으로 그 코드는 ClassCaseException이 발생할 일이 없는 타입 안전성을 보장하는 코드가 된다고 한다. 이제 비검사 경고를 제거하는 올바른 방법을 알아보자.
컴파일러의 도움을 받아 제거하라
대부분의 경고는 컴파일러가 알려준 대로 수정하면 사라진다.
Set<Coin> coinSet = new HashSet();
위 코드는 '매개변수화된 클래스 HashSet을 원시사용 했다.' 라는 경고가 발생한다. 매개변수화된 클래스는 제네릭 클래스를 나타낸다. 즉, 제네릭 클래스인데 타입 매개변수를 사용하지 않았다는 경고이다.
Set<Coin> coinSet = new HashSet<Coin>();
Set<Coin> coinSet = new HashSet<>(); // 컴파일러의 추론 기능 활용
위와 같이 타입 매개변수를 명시하여 경고를 해결할 수도 있지만 다이아몬드 연산자(<>) 만으로 해결할 수 있다. 컴파일러가 올바른 실제 타입 매개변수를 추론해주기 때문이다.
경고를 제거할 수 없지만 타입 안전함이 확신된다면 경고를 숨겨라
타입 관련 경고를 제거하려면 @SuppressWarnings("unchecked") 어노테이션 사용하면 된다. 이 어노테이션은 개별 지역변수 선언부터 클래스 전체까지 어떤 선언에도 달 수 있지만 가능한한 좁은 범위에 적용해야 한다. 보통 변수 선언, 아주 짧은 메서드, 생성자에 사용되며, 절대 클래스 전체에 적용해서는 안된다.
아래는 ArrayList의 toArray 메서드이다.
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
이 중 아래 코드에서 '확인되지 않는 형변환' 경고가 발생한다.
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
return 문에는 해당 어노테이션을 적용할 수 없으므로, 아래와 같이 변수를 선언하고 어노테이션을 적용해준다. 또한 경고를 무시해도 되는 안전한 이유를 항상 주석으로 남겨둔다.
public <T> T[] toArray(T[] a) {
if (a.length < size) {
// 생성한 배열과 매개변수로 받은 배열의 타입이 모두 T[]로 같으므로 올바른 형변환이다.
@SuppressWarnings("unchecked")
T[] result = (T[]) Arrays.copyOf(elementData, size, a.getClass());
return result;
}
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
SuppressWarnings 옵션
옵션 | 내용 |
all | 모든 경고 |
cast | 캐스트 연산자 경고 |
dep-ann | 사용하지 말아야할 주석 경고 |
deprecation | 사용하지 말아야할 메서드 경고 |
fallthrough | switch 문 break 누락 경고 |
finally | 반환하지 않은 finally 블럭 경고 |
null | null 경고 |
rawtypes | 제네릭을 사용하는 클래스 매개변수가 불특정일때 경고 |
unchecked | 검증되지 않은 연산자 경고 |
unused | 사용하지 않은 코드 관련 경고 |
정리
비검사 경고는 중요하니 무시하지 말자. 모든 비검사 경고는 런타임에 ClassCastException을 일으킬 수 있다. 경고를 없앨 방법을 찾지 못했다면, 그 코드가 타입 안전함을 증명하고 가능한 한 범위를 좁혀 @SuppressWarnings("unchecked") 어노테이션으로 경고를 숨기자. 그런 다음 경고를 숨긴 근거를 주석으로 남긴다.
'공부 > Effective Java' 카테고리의 다른 글
[Effective Java] Item 33. 타입 안전 이종 컨테이너를 고려하라 (0) | 2023.11.08 |
---|---|
[Effective Java] Item 31. 한정적 와일드카드를 사용해 API 유연성을 높이라 / feat. 매개변수화 타입 (1) | 2023.11.02 |
[Effective Java] Raw Type / 로 타입(Raw Type)은 사용하지 말라 (0) | 2023.10.10 |
[Effective Java] Item 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라 (0) | 2023.10.03 |
[Effective Java] Item 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라. (0) | 2023.10.02 |