이펙티브 자바) 규칙 59, 60, 61, 62, 63, 64, 65

2015. 7. 30. 01:44Language/Java

반응형

규칙 59. Avoid unnecessary use of checked exceptions


 checked exceptions

 Exception

 can recover

 IOException, 

IllegalArgumentExceptions

 uncheked exceptions

 RuntimeException

 can not recover

 transaction

NullPointerException


핵심요약 : checked exceptions 은 너무 남발하면 프로그램을 복잡하게 만든다.

그러므로 훌륭한 프로그래머가 되기위해서는 생각해보아야 한다. 무엇을?

1) 예외적인 상황을 막을수 있는가?   2) 예외 상황에 대한 조치를 취할 수 있는가?

막을수 없고, 조치를 취할 수 없다면(can't recover) runtimeException을 고려!

unchecked exception을 사용!(아래의 예제 참고~)

   // Invocation with checked exception

   try {

       obj.action(args);

   } catch(TheCheckedException e) {

       // Handle exceptional condition

       ...

   }

 

// Invocation with state-testing method and unchecked exception

   if (obj.actionPermitted(args)) {

       obj.action(args);

   } else {

       // Handle exceptional condition

       ...

   }



규칙 60Favor the use of standard exceptions

 어떤 예외를 재사용할 지 용도에 맞게 결정하여 사용하도록 하자!

  (단, 기존 예외의 하위 클래스를 자유로이 만들어 사용해도 좋다(규칙 63)


규칙 61Throw exceptions appropriate to the abstraction

     메서드가 하는 일고 뚜렷한 관련성이 없는 예외가 발생하면 당혹스럽다.

     해결방안

상위계층에서는 하위 계층에서 발생하는 예외를 반드시 받아서 상위계층 추상화 수준에 맞는 예외로 바꿔서 던져야 함.

이것을 exception translation 이라고 부른다.

예제1)


  // Exception Translation 

  try {

       // Use lower-level abstraction to do our bidding

       ...

   } catch(LowerLevelException e) {

       throw new HigherLevelException(...);

 }  


예제2) List<E> interface의 명세를 보면 예외변환이 필요함을 알수 있다.


public interface List<E> extends Collection<E>
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException if the index is out of range
* (<tt>index &lt; 0 || index &gt;= size()</tt>)
*/
E get(int index);


/**

* Returns the element at the specified position in this list. * 

@throws IndexOutOfBoundsException if the index is out of range * 

({@code index < 0 || index >= size()}).

*/

   public E get(int index) {

       ListIterator<E> i = listIterator(index);

       try {

           return i.next();

       } catch(NoSuchElementException e) {

           throw new IndexOutOfBoundsException("Index: " + index);  // exception translation 

       }

} 


예외 연결(exception chaining)

하위 계층의 예외를 상위 계층의 생성자에게 전달한다. 

Throwable.getCause를 호출하면 해당 정보를 꺼낼수 있으며 디버깅에 용이하다!


   // Exception with chaining-aware constructor

   class HigherLevelException extends Exception {

       HigherLevelException(Throwable cause) {

           super(cause);

       }

  }  


Throwable 

 initCause() to set cause, and getCause() to access cause.


규칙 62Document all exceptions thrown by each method

  • checked exceptions은 메서드의 throws 절에 나열
  • uncheked exceptions은 throws절에는 적지마라.

     프로그래머는 문서만 보면 무엇이 checked exception인지 무엇이 unchecked exception인지 알수 있다.


규칙 63Include failure-capture information in detail messages

    오류 정보를 알아내기 위해서는 오류의 상세메시지에 "예외에 관계된" 모든 인자와 필드의 값을 포함 시켜야 한다.

    간단히 말해 예외에서 나는 오류정보는 프로그래머 등의 사람들이 보게 되며, 이것은 오류를 쉽게 파악하거나 

    해결할 수 있게 해주는 메시지이다.


규칙 64. Strive for failure atomicity

    예외를 던지고 난 뒤에도 객체의 상태가 사용 가능한 상태로 남아 있으면 좋다.

    다시 말하면 메서드 호출이 정상적으로 처리되지 못한 객체의 상태는 메서드 호출 전 상태와 동일해야 한다.

    이것을 만족했을 때 이 메서드는 실패 원자성(failure atomicity)을 갖추었다고 한다.


실패 원자성을 달성하는 방법

  1. 가장 간단한 방법은 변경 불가능한 객체로 설계하는 것이다.(규칙 15) 또한, 변경 가능한 객체의 경우는 연산을 하기 전에 인자 유효성(validity)을 체크하는 것이 가장 보편적인 방법이다.(규칙 38) 
  2. computation so that any part that may fail takes place before any part that modifies the object / Compute the result first, to see whether there is an exception. If result is valid, then set it to the object.
  3. 연산 수행 도중에 발생하는 오류를 가로채는 복구 코드(recovery code)를 작성한다.
  4. 객체의 임시 복사본상에서 필요한 연산을 수행하고, 연산이 끝난 다음에 임시 복사본의 내용으로 객체 상태를 바꾸는 방법.


규칙 65.  Don’t ignore exceptions

    빈 catch 블록은 사용하지 말고, 최소한 예외를 무시해도 괜찮은 이유라도 주석으로 남겨둬라!


  // Empty catch block ignores exception - Highly suspect!

   try { ...

   } catch (SomeException e) {

   } 


-  END -






반응형