Programming/Java

[Effective Java] item27 - 비검사 경고를 제거하라

VSFe 2021. 3. 24. 18:24
비검사 경고를 제거하라!

제네릭을 사용하기 시작한 순간 컴파일러 경고와 친구가 될 것이다... 특히 제네릭에 익숙해지지 않으면 온갖 기묘한 경고들을 마주하게 될 것이다.

요즘은 인텔리제이같은 훌륭한 IDE가 있어서 수많은 경고를 미리 체크해주지만, 만약에 그런 IDE를 쓰지 않으면 javac 명령줄 인수에 -Xlint:unchecked를 붙여주자. (비검사 경고!)

Set<Lark> exaltation = new HashSet();

이런 코드가 있다고 하자. 딱 봐도 문제가 있는걸 깨달을 수 있을 것이다. 위에서 명시한 Xlint 옵션을 붙여주면 컴파일러에서 어떤식으로 수정해야 하는지 제시해주고, 그거에 맞춰 수정해주면 된다. (사실 IDE가 좋으면 알아서 고쳐준다.)

자바7 부터는 다이아몬드 연산자를 활용해 컴파일러가 실제 타입 매개변수를 추론해준다.

위에서 예시로 보여준 경고는 제거하기 쉬운 경우고, 실제로는 제거하기 상당히 어려운 경고도 흘러 넘친다! 그렇지만 최대한 모든 비검사 경고를 제거할 수 있도록 노력해보자! 그렇다면 코드가 타입 안정성도 보장되고, 런타임 과정에서 ClassCastException이 발생할 일도 없고, 의도한 대로 잘 동작하리라고 확신할수도 있다.

만약 경고를 제거하기 어렵지만 타입 안전하다고 확신한다면 @SuppressWarnings("unchecked")를 붙이자. 다만 이런다고 ClassCastException이 안 발생하는건 아니기에 차후 문제가 발생할 수 있고 생각지도 못한데서 추가적인 경고가 발생해도 전체를 다 Suppress 했기 때문에 확인도 못 한다!

조금 다행인 점은 @SuppressWarnings는 범위를 지정할 수 있다. 개별 지역변수 선언 부터 클래스 전체까지 범위를 정해 붙일 수 있다. 즉, 최대한 좁은 범위에 설정하여 다른 경고가 발생해도 무시되지 않도록 하자! 특히 한 줄이 넘는 메소드나 생성자에 해당 어노테이션이 필요하다면, 아예 코드 전체를 지역변수 선언 쪽으로 옮겨버리자. 귀찮고 번거로울 수 있지만 안정성 측면에선 그만한 값어치가 있다.

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에서 가져온 toArray 메소드이다. 이 친구를 컴파일 하면 Arrays.copyOf 부분에서 경고가 발생한다. (elements가 T 타입이라는 것이 컴파일 타임에선 보장되지 않으므로) 다만 return 문엔 어노테이션을 붙일 수 없으므로, 위에서 제시한대로 지역변수를 만들어보자.

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

이런식으로 하면 된다!

마지막으로, @SuppressWarnings("unchecked")을 사용하게 되면 그 경고를 무시해도 안전한 이유를 항상 주석으로 남겨야 한다. 다른 사람들이 해당 코드를 이해하는데에도 도움이 되고, 코드를 잘못 수정해서 타입 안전성을 잃어버리는 상황도 막아준다.