맞는데 왜 틀릴까..?

Java/Effective Java

[Effective Java] Item 69. 예외는 진짜 예외 상황에만 사용하라

안도일 2023. 8. 5. 17:54

예외는 오직 예외 상황에서만 써야 한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안된다.

 

 

부적절한 예 - 예외를 사용한 최적화

 

 

배열의 원소를 순회하는 표준적인 관용구

for (Mountain m : range)
	m.climb();

 

 

완전히 잘못 사용한 예외를 활용한 최적화

try{
    int i =0;
    while(true)
    	range[i++].climb();
} catch (ArrayIndexOutOfBoundsException e){
}

 

 

 

최적화를 위한 잘못된 추론

  • JVM은 배열에 접근할 때마다 경계를 넘지 않는지 검사한다.
  • 일반적인 반복문도 배열 경계에 도달하면 종료한다.
  • 따라서 경계를 넘는지 검사하는 일이 중복되므로 생략해 버리자.

 

추론이 잘못된 점

 

  1. 예외는 예외 상황에 쓸 용도로 설계되었으므로 명확한 검사만큼 빠르게 만들지 않았다.
  2. 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한된다.
  3. 배열을 순회하는 표준 관용구는 JVM이 알아서 최적화해주어서 앞서 걱정한 중복 검사를 수행하지 않는다.

 

 

  • 예외를 사용한 쪽이 표준 관용구보다 훨씬 느리다.
  • 반복문 안에 버그가 숨어 있다면 흐름 제어에 쓰인 예외가 이 버그를 숨겨 디버깅을 훨씬 어렵게 한다.

 

 

표준적이고 쉽게 이해되는 관용구를 사용하고, 성능 개선을 목적으로 과하게 머리를 쓴 기법은 자제하자.

실제로 성능이 개선되더라도 자바 플랫폼이 꾸준히 개선되고 있으니 최적화로 얻은 상대적인 성능 우위가 오래가지 않을 수 있다.

 

 

 

잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다.

 

 

  • 특정 상태나 조건에서만 호출할 수 있는 메서드를 제공하는 경우, 클라이언트가 이 조건을 확인할 수 있는 '상태 검사' 메서드도 함께 제공해야 한다.
  • 이렇게 함으로써 클라이언트는 메서드를 호출하기 전에 해당 조건이 충족되는지 확인하고, 조건이 충족되지 않은 상태에서의 호출을 방지할 수 있다.

 

 

Iterator 인터페이스

  • next : 상태 의존적 메서드 - Iterator 인터페이스의 next 메서드는 다음 요소를 반환하는 상태 의존적인 작업을 한다.
  • hasNext : 상태 검사 메서드 - 클라이언트는 next 메서드를 호출하기 전에 다음 요소가 있는지 확인하는 검사하는 hasNext 메서드도 필요하다.

 

만약 Iterator가 상태 검사 메서드 hasNext를 제공하지 않았다면 클라이언트가  대신해서 예외를 사용하여 상태 검사 메서드를 작성해야 할 것이다.

 

 

상태 검사 메서드 대신 사용할 수 있는 선택지

 

1. 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용하자. 상태 검사 메서드와 상태 의존적 메서드 호출 사이에 객체의 상태가 변할 수 있기 때문이다.

 

2. 성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복 수행한다면 옵셔널이나 특정 값을 선택하자.

 

3. 다른 모든 경우에는 상태 검사 메서드 방식이 조금 더 낫다. 가독성이 더 좋고, 잘못 사용했을 때 발견하기 쉽다.

 

 

결론

 

  • 예외는 예외 상황에서 쓸 의도로 설계된 없다.
  • 정상적인 제어 흐름에서 사용해서는 안되며, 이를 프로그래머에게 강요하는 API를 만들어서도 안된다.