들어가며
예외는 예외적인 상황에만 써야한다. 다음 코드를 보자.
1 | try { |
여기서 예외를 써서 반복문을 종료한 이유는 잘못된 추론을 근거로 성능을 높여보려 했기 때문이다. JVM은 배열에 접근할 때마다 경계를 넘지 않는지 검사하는데 일반적인 반복문도 배열 경계에 도달하면 종료한다. 따라서 이 검사를 반복문에도 명시하면 같은 일이 중복되므로 하나를 생략한 것이다. 하지만 이는 잘못되었다.
- 예외는 예외상황에 쓸 용도로 설계되었으므로 JVM 구현자 입장에서는 명확한 검사만큼 빠르게 만들어야할 동기가 약하다.
- 코드를 try-catch 블록안에 넣으면 JVM이 수행해주는 최적화에 제한이 걸린다.
- 배열을 순회하는 표준 관용구는 앞서 언급한 중복 검사를 수행하지 않는다. 즉 JVM이 알아서 최적화 해준다.
여기서 알 수 있듯이 예외는 예외적인 상황에만 써야한다. 절대로 일상적인 제어 흐름용으로 쓰여선 안된다. 이 원칙은 API 설계에도 적용된다. 잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야 한다. 특정 상태에서만 호출할 수 있는 상태 의존적 메소드를 제공하는 클래스는 상태 검사 메소드도 함께 제공해야한다. Iterator의 next와 hasNext가 그 예이다.
상태검사 메소드 대신 선택할 수 있는 방안도 몇개 있다. Optional 메소드라던가 return null이 그 예이다. 다음은 이를 선택하는 지침이다.
- 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 Optional이나 특정 값을 사용한다.
- 성능이 중요한 상황에서 상태 검사 메소드가 상태 의존적 메소드의 작업 일부를 중복 수행한다면 Optional이나 특정 값을 선택한다.
- 다른 모든 경우엔 상태 검사 메소드 방식이 조금 더 낫다.