development

확인 된 예외에 대한 사례

big-blog 2020. 2. 18. 22:50
반응형

확인 된 예외에 대한 사례


수년 동안 나는 다음 질문에 대한 적절한 대답을 얻을 수 없었습니다. 왜 일부 개발자는 확인 된 예외에 대해 그렇게합니까? 나는 수많은 대화를 나누고 블로그에서 글을 읽고 Bruce Eckel이 말한 것을 읽었습니다.

나는 현재 새로운 코드를 작성하고 있으며 예외를 처리하는 방법에 매우주의를 기울이고 있습니다. "체크 된 예외를 좋아하지 않습니다"군중의 관점을 보려고하는데 여전히 볼 수 없습니다.

내가 가진 모든 대화는 같은 질문으로 대답하지 않고 끝납니다 ... 설정하겠습니다.

일반적으로 (Java가 설계된 방식에서)

  • 오류는 절대로 잡히지 않아야하는 것들에 대한 것입니다 (VM에는 땅콩 알레르기가 있고 누군가 땅콩 병을 떨어 뜨 렸습니다)
  • RuntimeException은 프로그래머가 잘못한 것들에 대한 것입니다 (프로그래머가 배열의 끝을 벗어났습니다)
  • 예외 (런타임 예외 제외)는 프로그래머가 제어 할 수없는 것들에 대한 것입니다 (파일 시스템에 쓰는 동안 디스크가 가득 차고 프로세스의 파일 핸들 한계에 도달했으며 더 이상 파일을 열 수 없습니다)
  • Throwable은 단순히 모든 예외 유형의 상위입니다.

내가 듣는 일반적인 주장은 예외가 발생하면 모든 개발자가 프로그램을 종료한다는 것입니다.

내가 듣는 또 다른 일반적인 주장은 확인 된 예외로 인해 코드를 리팩터링하기가 더 어렵다는 것입니다.

"내가하려고하는 모든 것은 종료이다"라는 말에 대해, 나는 당신이 나가더라도 합리적인 오류 메시지를 표시해야한다고 말합니다. 오류를 처리하는 데 어려움을 겪고 있다면 이유를 분명하게 나타내지 않고 프로그램이 종료 될 때 사용자가 지나치게 만족하지 않을 것입니다.

"리팩토링하기가 어렵습니다"군중의 경우, 적절한 추상화 수준이 선택되지 않았 음을 나타냅니다. 메소드가 IOException을 발생시키는 것으로 선언하는 대신, IOException은 현재 상황에보다 적합한 예외로 변환되어야합니다.

프로그램을 정상적으로 종료 할 수 있도록 catch (Exception) (또는 경우에 따라 catch (Throwable))로 Main을 래핑하는 데 문제가 없습니다.하지만 항상 필요한 특정 예외를 포착합니다. 최소한 적절한 오류 메시지를 표시하십시오.

사람들이 대답하지 않는 질문은 다음과 같습니다.

예외 서브 클래스 대신 RuntimeException 서브 클래스를 처리하면 무엇을 잡아야하는지 어떻게 알 수 있습니까?

대답이 예외를 잡는 경우 시스템 예외와 같은 방식으로 프로그래머 오류를 처리합니다. 그것은 나에게 잘못 보인다.

Throwable을 잡으면 시스템 예외와 VM 오류 등을 같은 방식으로 처리합니다. 그것은 나에게 잘못 보인다.

대답은 당신이 알고있는 예외 만 잡는다는 것입니다. 그렇다면 어떤 예외가 발생했는지 어떻게 알 수 있습니까? 프로그래머 X가 새로운 예외를 던지고 그것을 포착하지 못한 경우 어떻게됩니까? 그것은 나에게 매우 위험한 것 같습니다.

스택 추적을 표시하는 프로그램이 잘못되었다고 말할 수 있습니다. 확인 된 예외를 좋아하지 않는 사람들은 그렇게 느끼지 않습니까?

따라서 확인 된 예외가 마음에 들지 않으면 왜 응답하지 않는 질문에 대답하지 않습니까?

편집 : 어느 모델을 사용해야 할 지에 대한 조언을 찾고 있지 않습니다. 제가 찾고있는 이유는 사람들이 Exception에서 확장하는 것을 좋아하지 않기 때문에 RuntimeException에서 확장하는 이유 및 / 또는 예외를 잡은 다음 RuntimeException을 다시 던지는 이유입니다. 그들의 방법에 던지기를 추가하십시오. 확인 된 예외를 싫어하는 동기를 이해하고 싶습니다.


나는 당신이했던 것과 같은 Bruce Eckel 인터뷰를 읽은 것 같아요. 항상 나를 괴롭 혔습니다. 실제로, 인터뷰 대상자는 .NET과 C #의 MS 천재 인 Anders Hejlsberg가 인터뷰 대상자에 의해 주장되었습니다.

http://www.artima.com/intv/handcuffs.html

내가 Hejlsberg와 그의 작품을 좋아하는 팬이지만,이 주장은 항상 나를 가짜로 부딪쳤다. 기본적으로 다음과 같이 요약됩니다.

"확인 된 예외는 프로그래머가 예외를 잡아서 무시함으로써 남용하기 때문에 문제가 숨겨 지거나 무시되어 사용자에게 표시 될 수 있기 때문에 예외입니다."

에 의해 "그렇지 않으면 사용자에게 제공" 당신이 런타임 예외를 사용하는 경우 내 말은 게으른 프로그래머는 그냥 (빈 catch 블록으로 잡는 대) 무시하고 사용자가 표시됩니다.

논쟁의 요약은 "프로그래머는 그것들을 제대로 사용하지 못하고 제대로 사용하지 않는 것이 그들을 갖지 않는 것보다 나쁘다"는 것이다.

이 주장에는 약간의 진실이 있으며 실제로 Java에서 운영자 재정의를하지 않는 Goslings 동기가 비슷한 주장에서 비롯된 것 같습니다. 프로그래머가 종종 학대를 당하기 때문에 프로그래머를 혼란스럽게합니다.

그러나 결국, 나는 그것이 Hejlsberg 's의 가짜 주장과 아마도 잘 알려진 결정보다는 부족을 설명하기 위해 만들어진 사후 적 주장을 발견합니다.

확인 된 예외를 과도하게 사용하는 것은 나쁜 일이며 사용자가 부주의하게 처리하는 경향이 있지만 API를 올바르게 사용하면 API 프로그래머가 API 클라이언트 프로그래머에게 큰 이점을 줄 수 있습니다.

이제 API 프로그래머는 모든 곳에서 확인 된 예외를 발생시키지 않도록주의해야합니다. 그렇지 않으면 클라이언트 프로그래머를 귀찮게합니다. 매우 게으른 클라이언트 프로그래머는 (Exception) {}Hejlsberg가 경고하고 모든 혜택을 잃어 버리고 지옥이 뒤 따르는 것에 따라 잡을 것입니다. 그러나 어떤 상황에서는 잘 확인 된 예외를 대신 할 수있는 방법이 없습니다.

나에게 고전적인 예는 파일 열기 API입니다. 언어 역사 (파일 시스템 이상)의 모든 프로그래밍 언어에는 파일을 열 수있는 API가 있습니다. 이 API를 사용하는 모든 클라이언트 프로그래머는 열려고하는 파일이 존재하지 않는 경우를 처리해야한다는 것을 알고 있습니다. 이 API를 사용하는 모든 클라이언트 프로그래머 는이 사례를 처리 해야한다는 것을 알아야 합니다. API 프로그래머가 주석만으로 처리해야 함을 알 수 있거나 실제로 클라이언트가 처리하도록 요구할 수 있습니다 .

C에서 관용구는

  if (f = fopen("goodluckfindingthisfile")) { ... } 
  else { // file not found ...

여기서 fopen0과 C를 반환하여 실패를 나타냅니다 (어리석게도) 0을 부울로 취급하고 기본적 으로이 관용구를 배우면 괜찮습니다. 그러나 당신이 멍청하고 관용구를 배우지 않으면 어떻게 될까요? 물론, 당신은 시작합니다

   f = fopen("goodluckfindingthisfile");
   f.read(); // BANG! 

어려운 방법을 배우십시오.

여기서는 강력한 형식의 언어에 대해서만 이야기하고 있습니다. 강력한 형식의 언어로 API가 무엇인지에 대한 명확한 아이디어가 있습니다. 각 언어에 대해 명확하게 정의 된 프로토콜과 함께 사용할 수있는 기능 (메소드)입니다.

명확하게 정의 된 프로토콜은 일반적으로 메소드 서명으로 정의됩니다. 여기서 fopen은 문자열 (또는 C의 경우 char *)을 전달해야합니다. 다른 것을 주면 컴파일 타임 오류가 발생합니다. 프로토콜을 따르지 않았습니다. API를 올바르게 사용하고 있지 않습니다.

일부 (불분명 한) 언어에서는 반환 유형도 프로토콜의 일부입니다. fopen()변수에 할당하지 않고 일부 언어에서 이에 상응 하는 함수 를 호출하려고 하면 컴파일 타임 오류가 발생합니다 (void 함수에서만 가능).

: 내가 만들려고 점이다 정적 타입 언어에서 API 프로그래머는 명백한 실수를하는 경우 컴파일에서 자신의 클라이언트 코드를 방지하여 제대로 API를 사용하도록 클라이언트를 권장합니다.

(루비와 같이 동적으로 유형이 지정된 언어에서는 파일 이름으로 플로트와 같은 것을 전달할 수 있으며 컴파일됩니다. 메소드 인수를 제어하지 않더라도 사용자에게 확인 된 예외가 발생하는 이유는 무엇입니까? 여기에 작성된 인수는 정적으로 유형이 지정된 언어에만 적용됩니다.)

그렇다면 확인 된 예외는 어떻습니까?

다음은 파일을 여는 데 사용할 수있는 Java API 중 하나입니다.

try {
  f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
  // deal with it. No really, deal with it!
  ... // this is me dealing with it
}

캐치 보이죠? 해당 API 메소드의 서명은 다음과 같습니다.

public FileInputStream(String name)
                throws FileNotFoundException

참고 FileNotFoundExceptionA는 확인 예외입니다.

API 프로그래머는 다음과 같이 말합니다. "이 생성자를 사용하여 새 FileInputStream을 만들 수 있지만

a) 파일 이름을 문자열로 전달 해야합니다.
b) 파일을 런타임에 찾지 못할 가능성을 수용 해야 합니다. "

그리고 그것이 내가 생각하는 한 요점입니다.

핵심은 기본적으로 질문이 "프로그래머의 통제를 벗어난 것"이라고 말하는 것입니다. 저의 첫 생각은 API 프로그래머가 통제 할 수없는 것들을 의미한다는 것입니다 . 그러나 실제로 올바르게 사용될 때 확인 된 예외는 실제로 클라이언트 프로그래머와 API 프로그래머가 제어하지 않는 것이어야합니다. 이것이 이것이 확인 된 예외를 남용하지 않는 열쇠라고 생각합니다.

파일 열기는 그 요점을 잘 보여줍니다. API 프로그래머는 API를 호출 할 때 존재하지 않는 파일 이름을 제공 할 수 있으며 원하는 것을 반환 할 수는 없지만 예외를 발생시켜야한다는 것을 알고 있습니다. 또한이 문제는 정기적으로 발생하며 클라이언트 프로그래머는 호출 할 때 파일 이름이 정확할 것으로 예상 할 수 있지만 제어 할 수없는 이유로 인해 런타임에 잘못 될 수 있음을 알고 있습니다.

그래서 API는 그것을 명시 적으로 만듭니다 : 당신이 전화 할 때이 파일이 존재하지 않고 당신이 그것을 잘 다루는 경우가 있습니다.

이것은 카운터 케이스와 함께 더 명확합니다. 테이블 API를 작성한다고 상상해보십시오. 이 방법을 포함하여 API가있는 테이블 모델이 있습니다.

public RowData getRowData(int row) 

API 프로그래머로서 일부 클라이언트가 테이블 외부의 행 또는 행 값에 대해 음수 값을 전달하는 경우가 있음을 알고 있습니다. 따라서 확인 된 예외를 던져서 클라이언트가 강제로 처리하도록 유혹 할 수 있습니다.

public RowData getRowData(int row) throws CheckedInvalidRowNumberException

(물론 "체크 됨"이라고 부르지는 않습니다.)

이것은 확인 된 예외를 잘못 사용하는 것입니다. 클라이언트 코드는 행 데이터를 가져 오기위한 호출로 가득 차 있습니다. 모두 시도 / catch를 사용해야하며 무엇을해야합니까? 잘못된 행을 찾았다 고 사용자에게보고합니까? 아마도 아닙니다-테이블 뷰를 둘러싼 UI가 무엇이든, 사용자가 불법 행이 요청되는 상태가되도록해서는 안됩니다. 그래서 그것은 클라이언트 프로그래머의 버그입니다.

API 프로그래머는 여전히 클라이언트가 이러한 버그를 코딩 할 것으로 예상하고와 같은 런타임 예외로 처리해야합니다 IllegalArgumentException.

에서 예외를 확인하면 getRowDataHejlsberg의 게으른 프로그래머가 단순히 빈 캐치를 추가하는 경우가 분명합니다. 이 경우 잘못된 행 값은 테스터 나 클라이언트 개발자에게조차 명확하지 않으며 대신 소스를 찾기 어려운 노크 오류가 발생합니다. 출시 후 Arianne 로켓이 폭발합니다.

문제가 있습니다. 확인 된 예외 FileNotFoundException는 클라이언트 프로그래머에게 가장 유용한 방법으로 API를 정의하는 API 프로그래머 도구 상자의 좋은 도구 일뿐 만 아니라 필수 도구 라고 말하고 있습니다. 그러나 CheckedInvalidRowNumberException큰 불편은 프로그래밍에 나쁜 영향을 미치므로 피해야합니다. 그러나 차이점을 말하는 방법.

나는 그것이 정확한 과학이 아니라고 생각하며 Hejlsberg의 주장에 어느 정도 근거하고 아마도 정당화 될 것이라고 생각합니다. 그러나 나는 여기에 목욕물을 가지고 아기를 버리는 것이 기쁘지 않으므로 여기에서 규칙을 추출하여 좋은 예외를 나쁜 것과 구별 할 수있게하십시오.

  1. 고객의 통제 불능 또는 폐쇄 대 개방 :

    확인 된 예외는 오류 사례가 API 클라이언트 프로그래머 가 제어 할 수없는 경우에만 사용해야합니다 . 이것은 시스템의 개방 또는 폐쇄 와 관련이 있습니다. 클라이언트 프로그래머가 테이블 뷰 (폐쇄 된 시스템)에서 행을 추가하고 삭제하는 모든 버튼, 키보드 명령 등을 제어 할 수 있는 제한된 UI에서 데이터를 가져 오려고 시도하면 클라이언트 프로그래밍 버그입니다. 존재하지 않는 행. 임의의 수의 사용자 / 응용 프로그램이 파일을 추가 및 삭제할 수있는 파일 기반 운영 체제 (개방형 시스템)에서 클라이언트가 요청한 파일이 지식없이 삭제되었으므로 처리해야합니다. .

  2. 편재:

    클라이언트가 자주 수행하는 API 호출에는 확인 된 예외를 사용하지 않아야합니다. 나는 종종 클라이언트 코드의 많은 곳에서 시간이 아닌 자주 의미합니다. 따라서 클라이언트 코드는 동일한 파일을 많이 열려고하지 않지만 내 테이블보기는 RowData다른 방법에서 온통 얻을 수 있습니다. 특히, 나는 다음과 같은 많은 코드를 작성할 것입니다

    if (model.getRowData().getCell(0).isEmpty())
    

    그리고 매번 시도 / 잡기를 감싸는 것이 고통 스러울 것입니다.

  3. 사용자에게 알리기 :

    최종 사용자에게 유용한 오류 메시지가 표시 될 수있는 경우 확인 된 예외를 사용해야합니다. 이것이 "어떻게됩니까?" 위의 질문. 또한 항목 1과 관련이 있습니다. 클라이언트 API 시스템 외부의 파일로 인해 파일이 없을 수 있다고 예상 할 수 있으므로 사용자에게 다음과 같이 합리적으로 알려줄 수 있습니다.

    "Error: could not find the file 'goodluckfindingthisfile'"
    

    불법 행 번호는 내부 버그와 사용자의 결함으로 인해 발생했기 때문에 실제로 제공 할 수있는 유용한 정보는 없습니다. 앱에서 런타임 예외가 콘솔로 넘어 가지 않도록하면 아마도 다음과 같은 추악한 메시지가 나타납니다.

    "Internal error occured: IllegalArgumentException in ...."
    

    간단히 말해, 클라이언트 프로그래머가 사용자에게 도움이되는 방식으로 예외를 설명 할 수 없다고 생각되면 체크 된 예외를 사용하지 않아야합니다.

이것이 저의 규칙입니다. 약간의 노력이 있었으며 의심 할 여지없이 예외가있을 것입니다 (원한다면 수정 해주세요). 그러나 내 주요 주장은 FileNotFoundException확인 된 예외가 매개 변수 유형만큼 API 계약의 중요하고 유용한 경우와 같은 경우가 있다는 것 입니다. 따라서 잘못 사용되었다고해서 포기해서는 안됩니다.

미안하지만, 이것을 너무 길고 와플하게 만들려는 것은 아닙니다. 두 가지 제안으로 마무리하겠습니다.

A : API 프로그래머 : 유용성을 유지하기 위해 점검 된 예외를 거의 사용하지 마십시오. 의심스러운 경우 검사되지 않은 예외를 사용하십시오.

B : 클라이언트 프로그래머 : 개발 초기에 랩핑 된 예외 (Google 예외)를 만드는 습관을들이십시오. JDK 1.4 이상에서는이를 RuntimeException위한 생성자를 제공 하지만 자신도 쉽게 생성 할 수 있습니다. 생성자는 다음과 같습니다.

public RuntimeException(Throwable cause)

그런 다음 체크 된 예외를 처리해야하고 게으른 느낌이 들거나 (예 : API 프로그래머가 체크 된 예외를 처음 사용하는 데 지나치게 열중했다고 생각하는 경우) 습관을 들이고 예외를 삼키지 말고 감싸십시오. 다시 던져.

try {
  overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
  throw new RuntimeException(exception);  
}

이것을 IDE의 작은 코드 템플릿 중 하나에 넣고 게으른 느낌이들 때 사용하십시오. 이렇게하면 확인 된 예외를 실제로 처리 해야하는 경우 런타임에 문제를 확인한 후 다시 돌아와서 처리해야합니다. 저를 믿으십시오 (Anders Hejlsberg), 당신은 결코 그 TODO로 돌아 오지 않을 것입니다.

catch (Exception e) { /* TODO deal with this at some point (yeah right) */}

확인 된 예외에 대한 것은 개념을 일반적으로 이해함으로써 실제로 예외가 아니라는 것입니다. 대신 API 대체 반환 값입니다.

예외에 대한 전체적인 아이디어는 콜 체인에서 어딘가에 발생하는 오류가 개입하여 코드에 대해 걱정할 필요없이 버블 링되어 어딘가에있는 코드에 의해 처리 될 수 있다는 것입니다. 반면, 확인 된 예외는 던지기와 포수 간의 모든 수준의 코드가 예외를 통과 할 수있는 모든 형태의 예외에 대해 알고 있음을 선언해야합니다. 확인 된 예외가 단순히 호출자가 확인 해야하는 특별한 반환 값 인 경우 실제로는 거의 다릅니다. 예. [의사 코드] :

public [int or IOException] writeToStream(OutputStream stream) {
    [void or IOException] a= stream.write(mybytes);
    if (a instanceof IOException)
        return a;
    return mybytes.length;
}

Java는 대체 반환 값 또는 간단한 인라인 튜플을 반환 값으로 수행 할 수 없으므로 확인 된 예외는 합리적인 응답입니다.

문제는 표준 라이브러리의 큰 스 와이드를 포함하여 많은 코드가 여러 수준을 잘 잡을 수있는 예외적 인 상황에서 예외를 잘못 사용한다는 것입니다. IOException이 RuntimeException이 아닌 이유는 무엇입니까? 다른 모든 언어에서는 IO 예외가 발생하도록 할 수 있으며 처리 할 작업이 없으면 응용 프로그램이 중지되고 편리한 스택 추적을 볼 수 있습니다. 이것이 일어날 수있는 가장 좋은 일입니다.

예제에서 두 개의 메소드를 사용하여 전체 스트림 작성 프로세스에서 모든 IOException을 포착하고 프로세스를 중단하고 오류보고 코드로 이동하십시오. Java에서는 모든 호출 레벨, 심지어 IO가없는 레벨까지 'throws IOException'을 추가하지 않으면 그렇게 할 수 없습니다. 이러한 메소드는 예외 처리에 대해 알 필요가 없습니다. 서명에 예외를 추가 해야하는 경우 :

  1. 불필요하게 커플 링을 증가시킨다;
  2. 인터페이스 서명을 변경하기 매우 취약하게 만듭니다.
  3. 코드를 읽기 어렵게 만듭니다.
  4. 일반적인 프로그래머의 반응은 'throws Exception', 'catch (Exception e) {}'와 같은 끔찍한 일을하거나 RuntimeException (디버깅을 더 어렵게 함)에 모든 것을 감싸서 시스템을 물리 치는 것입니다.

그리고 다음과 같은 어리석은 라이브러리 예외가 많이 있습니다.

try {
    httpconn.setRequestMethod("POST");
} catch (ProtocolException e) {
    throw new CanNeverHappenException("oh dear!");
}

이렇게 우스꽝스러운 코드로 코드를 복잡하게 만들어야 할 때 실제로 이것이 단순한 API 디자인이 아니더라도 확인 된 예외가 많은 증오를받는 것은 놀라운 일이 아닙니다.

또 다른 특별한 나쁜 영향은 구성 요소 A가 일반 구성 요소 B에 콜백을 제공하는 제어 반전에 있습니다. 구성 요소 A는 콜백에서 구성 요소 B라고하는 위치로 예외를 다시 전달할 수 있기를 원합니다. B에 의해 수정 된 콜백 인터페이스를 변경하기 때문입니다. A는 예외를 더 많이 처리하는 상용구 인 RuntimeException에 실제 예외를 랩핑하여 처리 할 수 ​​있습니다.

Java 및 표준 라이브러리에서 구현 된 확인 된 예외는 상용구, 상용구, 상용구를 의미합니다. 이미 장황한 언어에서는 이것이 승리가 아닙니다.


확인 된 예외에 대해 모든 (다수) 이유를 다시 해치지 않고 하나만 선택하겠습니다. 이 코드 블록을 작성한 횟수를 잃었습니다.

try {
  // do stuff
} catch (AnnoyingcheckedException e) {
  throw new RuntimeException(e);
}

99 %의 시간 동안 나는 그것에 대해 아무것도 할 수 없습니다. 마지막으로 블록은 필요한 정리 작업을 수행합니다.

나는 또한 이것을 본 횟수를 잃어 버렸다.

try {
  // do stuff
} catch (AnnoyingCheckedException e) {
  // do nothing
}

왜? 누군가가 그것을 다루어야하고 게으 르기 때문에. 틀렸어? 확실한. 그런가요? 물론. 이것이 검사되지 않은 예외 인 경우 어떻게됩니까? 앱이 방금 사망했을 것입니다 (예외를 삼키는 것이 좋습니다).

그런 다음 java.text.Format 처럼 흐름 제어의 형태로 예외를 사용하는 분노한 코드 가 있습니다. Bzzzt. 잘못된. "abc"를 양식의 숫자 필드에 넣는 사용자는 예외가 아닙니다.

좋아, 그게 세 가지 이유 인 것 같아.


나는 이것이 오래된 질문이라는 것을 알고 있지만 확인 된 예외로 레슬링을 보내고 있으며 추가 할 것이 있습니다. 그것의 길이 나를 용서 해주세요!

확인 된 예외를 가진 나의 주요 쇠고기는 다형성을 망치는 것입니다. 다형성 인터페이스로 멋지게 연주하는 것은 불가능합니다.

좋은 자바 List인터페이스를 가져 가라 . 우리는 ArrayListand 같은 일반적인 인 메모리 구현을 가지고 있습니다 LinkedList. 또한 AbstractList새로운 유형의 목록을 쉽게 디자인 할 수 있는 골격 클래스 가 있습니다. 읽기 전용 목록의 경우 두 가지 방법 만 구현하면됩니다 : size()get(int index).

이 예제 WidgetList클래스 Widget는 파일에서 유형이 아닌 고정 크기 객체를 읽습니다 .

class WidgetList extends AbstractList<Widget> {
    private static final int SIZE_OF_WIDGET = 100;
    private final RandomAccessFile file;

    public WidgetList(RandomAccessFile file) {
        this.file = file;
    }

    @Override
    public int size() {
        return (int)(file.length() / SIZE_OF_WIDGET);
    }

    @Override
    public Widget get(int index) {
        file.seek((long)index * SIZE_OF_WIDGET);
        byte[] data = new byte[SIZE_OF_WIDGET];
        file.read(data);
        return new Widget(data);
    }
}

익숙한 List인터페이스를 사용하여 위젯을 노출하면 자체 를 알 필요없이 항목을 검색 list.get(123)하거나 ( for (Widget w : list) ...) 목록을 반복 할 수 있습니다 ( ) WidgetList. 일반 목록을 사용하는 표준 메소드에이 목록을 전달하거나로 묶을 수 Collections.synchronizedList있습니다. 이 코드를 사용하는 코드는 "위젯"이 그 자리에서 만들어 졌는지, 어레이에서 가져 왔는지, 또는 파일이나 데이터베이스에서 읽거나 네트워크를 통해 읽거나 미래의 서브 스페이스 릴레이에서 읽거나 알 필요가 없습니다. List인터페이스가 올바르게 구현 되었으므로 여전히 올바르게 작동합니다 .

그렇지 않다는 것을 제외하고. 위의 클래스는 파일 액세스 메소드가 IOException"캐치 또는 지정"해야하는 확인 된 예외를 발생 시킬 수 있으므로 컴파일되지 않습니다 . 던진 것으로 지정할는 없습니다 . 컴파일러는 List인터페이스 계약을 위반하기 때문에 허용하지 않습니다 . 그리고 WidgetList나중에 설명 할 것처럼 자체적으로 예외를 처리 할 수있는 유용한 방법은 없습니다 .

분명히 할 일은 확인되지 않은 예외로 catch 된 예외를 catch하고 다시 던지는 것입니다.

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw new WidgetListException(e);
    }
}

public static class WidgetListException extends RuntimeException {
    public WidgetListException(Throwable cause) {
        super(cause);
    }
}

((편집 : Java 8 UncheckedIOException은이 경우를 위해 클래스를 추가했습니다 : IOException다형성 메소드 경계에서 s 를 잡아서 다시 던지기위한 것 입니다.)

따라서 확인 된 예외 이와 같은 경우에는 작동하지 않습니다 . 당신은 그들을 던질 수 없습니다. Map데이터베이스에 의해 뒷받침되는 영리함을위한 Ditto 또는 java.util.RandomCOM 포트를 통해 양자 엔트로피 소스 연결된 구현 . 다형성 인터페이스의 구현으로 새로운 것을 시도하자마자 확인 된 예외 개념이 실패합니다. 그러나 확인 된 예외는 너무 교활하여 여전히 안전하지 않습니다. 낮은 수준의 메소드를 잡아서 다시 던져서 코드를 어수선하게하고 스택 추적을 어수선하게해야하기 때문입니다.

유비쿼터스 Runnable인터페이스가 확인 된 예외를 던지는 것을 호출하면 종종이 코너로 돌아갑니다. 예외를 그대로 던질 수는 없으므로로 잡아서 다시 던져서 코드를 복잡하게 만들 수 있습니다 RuntimeException.

실제로 해킹에 의존하면 선언되지 않은 확인 된 예외를 던질 있습니다 . 런타임시 JVM은 확인 된 예외 규칙을 신경 쓰지 않으므로 컴파일러 만 바보로 만들어야합니다. 가장 쉬운 방법은 제네릭을 남용하는 것입니다. 이것은 내 메소드입니다 (Java 8 이전에 일반 메소드의 호출 구문에 필요하기 때문에 클래스 이름이 표시됨).

class Util {
    /**
     * Throws any {@link Throwable} without needing to declare it in the
     * method's {@code throws} clause.
     * 
     * <p>When calling, it is suggested to prepend this method by the
     * {@code throw} keyword. This tells the compiler about the control flow,
     * about reachable and unreachable code. (For example, you don't need to
     * specify a method return value when throwing an exception.) To support
     * this, this method has a return type of {@link RuntimeException},
     * although it never returns anything.
     * 
     * @param t the {@code Throwable} to throw
     * @return nothing; this method never returns normally
     * @throws Throwable that was provided to the method
     * @throws NullPointerException if {@code t} is {@code null}
     */
    public static RuntimeException sneakyThrow(Throwable t) {
        return Util.<RuntimeException>sneakyThrow1(t);
    }

    @SuppressWarnings("unchecked")
    private static <T extends Throwable> RuntimeException sneakyThrow1(
            Throwable t) throws T {
        throw (T)t;
    }
}

만세! 이것을 사용하여 선언하지 않고 스택에 깊이를 검사 RuntimeException하지 않고 스택 추적 을 래핑 하지 않고 스택 추적을 혼란스럽게 만들 수 있습니다! "WidgetList"예제를 다시 사용하여 :

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw sneakyThrow(e);
    }
}

불행히도, 확인 된 예외의 최종 모욕은 결함이 있다고 판단 할 수없는 경우 컴파일러 가 확인 된 예외 잡을 수 없도록 거부한다는 것 입니다. (체크되지 않은 예외에는이 규칙이 없습니다.) 교묘하게 던져진 예외를 포착하려면 다음을 수행해야합니다.

try {
    ...
} catch (Throwable t) { // catch everything
    if (t instanceof IOException) {
        // handle it
        ...
    } else {
        // didn't want to catch this one; let it go
        throw t;
    }
}

다소 어색하지만 플러스 측면에서는에 싸여있는 확인 된 예외를 추출하는 코드보다 약간 간단합니다 RuntimeException.

유감스럽게도, 예외를 다시 던지는 것에 대해 Java 7에 추가 된 규칙 덕분에 throw t;유형 t이 확인 되었더라도 여기에 해당 내용이 합법적 입니다.


확인 된 예외가 다형성을 충족하면 반대의 경우도 문제가됩니다. 메소드가 잠재적으로 확인 된 예외를 throw하는 것으로 지정되었지만 재정의 된 구현은 그렇지 않습니다. 예를 들어, 추상 클래스 OutputStreamwrite메소드는 모두 지정 throws IOException합니다. ByteArrayOutputStream실제 I / O 소스 대신 메모리 내 배열에 쓰는 서브 클래스입니다. 재정의 된 write메소드는 IOExceptions를 유발할 수 없으므로 throws절이 없으므로 catch-or-specify 요구 사항에 대한 걱정없이 호출 할 수 있습니다.

항상 그렇지는 않습니다. Widget스트림에 저장하는 방법이 있다고 가정하십시오 .

public void writeTo(OutputStream out) throws IOException;

이 방법으로 평범한 것을 받아들이 OutputStream는 것이 옳은 일이므로 파일, 데이터베이스, 네트워크 등 모든 종류의 출력에 다형성으로 사용할 수 있습니다. 그리고 메모리 내 배열. 그러나 메모리 내 배열을 사용하면 실제로 발생할 수없는 예외를 처리해야하는 가짜 요구 사항이 있습니다.

ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
    someWidget.writeTo(out);
} catch (IOException e) {
    // can't happen (although we shouldn't ignore it if it does)
    throw new RuntimeException(e);
}

평소와 같이 확인 된 예외가 발생합니다. 변수가 개방형 예외 요구 사항이 더 많은 기본 유형으로 선언 된 경우 응용 프로그램에서 해당 예외 가 발생하지 않는다는 것을 알고 있더라도 해당 예외에 대한 처리기를 추가 해야합니다.

그러나 확인 된 예외는 실제로 너무 성가 시므로 반대의 경우도 마찬가지입니다! 현재 에 대한 호출에 IOException의해 발생 된 모든 것을 포착한다고 가정 하지만 변수의 선언 된 유형을로 변경하려는 경우 컴파일러는 처리 할 수 ​​없다고 확인 된 예외를 포착하려고 시도하는 데 대해 요금을 청구합니다.writeOutputStreamByteArrayOutputStream

이 규칙은 일부 터무니없는 문제를 일으 킵니다. 예를 들어, 세 가지 중 하나 write의 방법 OutputStream입니다 하지 에 의해 오버라이드 (override) ByteArrayOutputStream. 특히, 오프셋 0과 배열 길이 write(byte[] data)로 호출하여 전체 배열을 작성하는 편리한 방법입니다 write(byte[] data, int offset, int length). ByteArrayOutputStream3 인수 메서드를 재정의하지만 1 인수 편의 메서드를 그대로 상속합니다. 상속 된 메소드는 옳은 일을하지만 원치 않는 throws절을 포함합니다 . 그것은 아마도의 디자인에 대한 감독일지도 ByteArrayOutputStream모르지만 예외를 잡는 코드와의 소스 호환성을 깨뜨릴 수 있기 때문에 그것을 고칠 수는 없습니다. 예외는 결코, 결코, 결코 던져지지 않을 것입니다!

그 규칙은 편집과 디버깅 중에도 성가시다. 예를 들어, 때로는 메소드 호출을 일시적으로 주석 처리하고 확인 된 예외가 발생했을 경우 컴파일러는 이제 로컬 trycatch블록 의 존재에 대해 불평 합니다. 그래서 나도 그 주석해야하고, 내 코드를 편집 할 때 때문에 이제 IDE가 잘못된 수준으로 들여 쓰기합니다 {및이 }주석하고 있습니다. 가! 작은 불만이지만 확인 된 예외를 확인하는 유일한 방법은 문제를 일으키는 것 같습니다.


거의 다 했어요. 확인 된 예외에 대한 나의 최종 좌절 은 대부분의 콜 사이트 에서 그들과 함께 할 수있는 유용한 것이 없다는 것입니다. 이상이 발생하면 사용자에게 문제를 알리거나 작업을 적절하게 종료하거나 재 시도 할 수있는 유능한 응용 프로그램 별 처리기가 있습니다. 전체 목표를 아는 유일한 핸들러이기 때문에 스택을 높은 처리기 만 처리 할 수 ​​있습니다.

대신 우리는 다음 관용구를 얻습니다.이 관용구는 컴파일러를 종료하는 방법으로 만연합니다.

try {
    ...
} catch (SomeStupidExceptionOmgWhoCares e) {
    e.printStackTrace();
}

GUI 또는 자동 프로그램에서 인쇄 된 메시지가 표시되지 않습니다. 더 나쁜 것은 예외 후에 나머지 코드로 진행됩니다. 예외는 실제로 오류가 아닌가? 그런 다음 인쇄하지 마십시오. 그렇지 않으면 다른 예외가 순간적으로 날아가고 그 시간까지 원래 예외 객체가 사라집니다. 이 관용구는 BASIC On Error Resume Next이나 PHP 보다 낫지 않습니다 error_reporting(0);.

어떤 종류의 로거 클래스를 호출하는 것은 그리 좋지 않습니다.

try {
    ...
} catch (SomethingWeird e) {
    logger.log(e);
}

그것은 e.printStackTrace();불확실한 상태의 코드와 마찬가지로 게으 르며 여전히 쟁기질입니다. 또한 특정 로깅 시스템 또는 다른 처리기의 선택은 응용 프로그램에 따라 다르므로 코드 재사용이 손상됩니다.

하지만 기다려! 응용 프로그램 별 처리기를 찾는 가장 쉽고 보편적 인 방법이 있습니다. 호출 스택보다 높거나 Thread의 catch되지 않은 예외 처리기 로 설정되어 있습니다. 따라서 대부분의 장소 에서 예외를 스택 위로 던지기 만하면 됩니다. 예, throw e;. 확인 된 예외는 방해가됩니다.

나는 언어가 디자인되었을 때 확인 된 예외가 좋은 생각처럼 들릴 것이라고 확신하지만 실제로는 그것들이 모두 귀찮고 아무런 이점이 없다는 것을 알았습니다.


글쎄, 그것은 스택 트레이스를 표시하거나 자동으로 충돌하는 것이 아닙니다. 계층 간 오류를 전달할 수 있다는 것입니다.

확인 된 예외의 문제점은 사람들이 중요한 세부 사항 (즉, 예외 클래스)을 삼키도록 권장한다는 것입니다. 그 세부 사항을 삼키지 않기로 선택한 경우 전체 앱에서 던지기 선언을 계속 추가해야합니다. 이는 1) 새로운 예외 유형이 많은 함수 시그니처에 영향을 미치며, 2) 실제로 포착하려는 예외의 특정 인스턴스를 놓칠 수 있음을 의미합니다 (예 : 데이터를 보조 파일은 선택 사항이므로 오류를 무시할 수 있지만 서명 throws IOException이므로이를 간과하기 쉽습니다).

실제로 응용 프로그램 에서이 상황을 처리하고 있습니다. 거의 예외를 AppSpecificException으로 다시 패키지했습니다. 이로 인해 서명이 정말 깨끗해졌으며 서명 폭발 throws대해 걱정할 필요가 없었습니다 .

물론, 이제 재시도 로직 등을 구현하여 더 높은 수준의 오류 처리를 전문화해야합니다. 그러나 모든 것은 AppSpecificException이므로 "IOException이 발생하면 다시 시도하십시오"또는 "ClassNotFound가 발생하면 완전히 중단하십시오"라고 말할 수 없습니다. 우리는 코드와 타사 코드 사이를 통과하면서 물건이 계속해서 다시 포장되기 때문에 실제 예외에 도달 할 수있는 확실한 방법이 없습니다 .

이것이 내가 파이썬에서 예외 처리를 좋아하는 이유입니다. 당신이 원하는 것 그리고 / 또는 처리 할 수있는 것만 잡을 수 있습니다. 마치 자신을 다시 던지는 것처럼 다른 모든 것은 거품이납니다 (어쨌든 해냈습니다).

필자는 여러 번 발견했으며, 언급 한 프로젝트 전체에서 예외 처리는 3 가지 범주로 분류됩니다.

  1. 특정 예외를 포착하고 처리하십시오 . 예를 들어 재시도 로직을 구현하는 것입니다.
  2. 다른 예외를 잡아서 다시 던지십시오 . 여기서 일어나는 모든 일은 보통 로깅이며, "$ filename을 열 수 없습니다"와 같은 간단한 메시지입니다. 이것들은 당신이 할 수없는 오류입니다. 더 높은 수준 만이 그것을 처리 할만큼 충분히 알고 있습니다.
  3. 모든 것을 잡아서 오류 메시지를 표시합니다. 이것은 일반적으로 디스패처의 루트에 있으며 예외가 아닌 메커니즘 (팝업 대화 상자, RPC 오류 객체 마샬링 등)을 통해 호출자에게 오류를 전달할 수 있도록합니다.

SNR

먼저, 확인 된 예외는 코드의 "신호 대 잡음비"를 감소시킵니다. Anders Hejlsberg도 비슷한 개념 인 명령형 대 선언 형 프로그래밍에 대해 설명합니다. 어쨌든 다음 코드 스 니펫을 고려하십시오.

Java에서 비 UI 스레드에서 UI를 업데이트하십시오.

try {  
    // Run the update code on the Swing thread  
    SwingUtilities.invokeAndWait(() -> {  
        try {
            // Update UI value from the file system data  
            FileUtility f = new FileUtility();  
            uiComponent.setValue(f.readSomething());
        } catch (IOException e) {  
            throw new UncheckedIOException(e);
        }
    });
} catch (InterruptedException ex) {  
    throw new IllegalStateException("Interrupted updating UI", ex);  
} catch (InvocationTargetException ex) {
    throw new IllegalStateException("Invocation target exception updating UI", ex);
}

C #에서 UI가 아닌 스레드에서 UI를 업데이트하십시오.

private void UpdateValue()  
{  
   // Ensure the update happens on the UI thread  
   if (InvokeRequired)  
   {  
       Invoke(new MethodInvoker(UpdateValue));  
   }  
   else  
   {  
       // Update UI value from the file system data  
       FileUtility f = new FileUtility();  
       uiComponent.Value = f.ReadSomething();  
   }  
}  

나에게 훨씬 더 분명한 것 같습니다. Swing에서 점점 더 많은 UI 작업을 시작하면 확인 된 예외가 실제로 성 가시고 쓸모 없게됩니다.

감옥 휴식

Java의 List 인터페이스와 같은 가장 기본적인 구현을 구현하기 위해 계약에 의한 설계 도구로 예외를 확인했습니다. 데이터베이스 나 파일 시스템 또는 점검 된 예외를 발생시키는 다른 구현으로 지원되는 목록을 고려하십시오. 유일하게 가능한 구현은 점검 된 예외를 포착하여 점검되지 않은 예외로 다시 발생시키는 것입니다.

@Override
public void clear()  
{  
   try  
   {  
       backingImplementation.clear();  
   }  
   catch (CheckedBackingImplException ex)  
   {  
       throw new IllegalStateException("Error clearing underlying list.", ex);  
   }  
}  

이제 모든 코드의 요점이 무엇인지 물어봐야합니다. 확인 된 예외는 소음을 추가하고 예외가 포착되었지만 처리되지 않았으며 계약에 의한 설계 (확인 된 예외 측면에서)가 고장났습니다.

결론

  • 예외 포착은 처리와 다릅니다.
  • 확인 된 예외는 코드에 노이즈를 추가합니다.
  • 예외 처리는 C #에서 잘 작동합니다.

나는 이것 대해 이전에 블로그했다 .


Artima 는 .NET의 건축가 중 한 명인 Anders Hejlsberg 와의 인터뷰를 통해 확인 된 예외에 대한 주장을 심각하게 다루고 있습니다. 짧은 맛보는 사람 :

throws 절은 적어도 Java로 구현되는 방식으로 예외를 처리하도록 강제하지는 않지만 예외를 처리하지 않으면 예외를 통과 할 수있는 정확한 예외를 인정해야합니다. 선언 된 예외를 잡아 내거나 자신의 throws 절에 넣어야합니다. 이 요구 사항을 해결하기 위해 사람들은 우스운 일을합니다. 예를 들어, 그들은 "예외를 던지다"라고 모든 방법을 장식합니다. 그것은 그 기능을 완전히 무너 뜨리고, 프로그래머가 좀 더 거친 글을 쓰도록 만들었습니다. 그것은 아무도 도움이되지 않습니다.


나는 항상 체크 된 예외를 선호했기 때문에 처음에 당신과 동의했으며 .Net에서 예외를 확인하지 않은 이유에 대해 생각하기 시작했습니다. 그러나 나는 확인 된 예외와 같은 사실이 아니라는 것을 깨달았습니다.

당신이 질문에 대답하기 위해, 예, 나는 내 프로그램이 스택 추적을 보여주기를 좋아합니다. 응용 프로그램을보고 싶었던 가장 추악한 오류 메시지의 힙으로 폭발하기를 원합니다.

그리고 그 이유는 그것이 가능하다면 고쳐야하고 바로 고쳐야하기 때문입니다. 문제가 있음을 즉시 알고 싶습니다.

실제로 몇 번이나 예외를 처리합니까? 예외를 잡는 것에 대해 말하는 것이 아니라 예외를 다루는 것에 대해 말하는 것입니까? 다음을 작성하는 것이 너무 쉽습니다.

try {
  thirdPartyMethod();
} catch(TPException e) {
  // this should never happen
}

그리고 당신이 나쁜 습관이라고 말할 수 있다는 것을 알고 있습니다. '답변'은 예외로 무언가를하는 것입니다 (내가 추측하고, 기록 하시겠습니까?)하지만 실제 세계 (tm)에서는 대부분의 프로그래머는하지 않습니다. 그것.

그렇습니다. 그렇지 않으면 예외를 잡기를 원하지 않습니다. 나를 조일 때 프로그램이 크게 폭발하기를 원합니다. 조용히 실패하는 것이 최악의 결과입니다.


Effective Java Exceptions 기사 는 체크되지 않은 상태를 사용하는시기와 체크 된 예외를 사용하는시기를 잘 설명합니다. 주요 기사를 강조하기 위해 해당 기사에서 인용 한 내용은 다음과 같습니다.

우발성 : 방법의 의도 된 목적으로 표현 될 수있는 방법으로부터 대안적인 반응을 요구하는 예상 조건. 이 방법의 호출자는 이러한 종류의 조건을 예상하고 이에 대처하기위한 전략을 가지고 있습니다.

결함 : 메소드의 내부 구현을 참조하지 않고는 설명 할 수없는 의도 된 목적을 달성 할 수없는 계획되지 않은 조건.

(SO는 테이블을 허용하지 않으므로 원본 페이지 에서 다음을 읽을 수 있습니다 ...)

우연성

  • 다음으로 간주됩니다 : 디자인의 일부
  • 일어날 것으로 예상됩니다 : 정기적이지만 드물게
  • 누가 신경 쓰는지 : 메소드를 호출하는 업스트림 코드
  • 예 : 대체 리턴 모드
  • 최상의 매핑 : 확인 된 예외

결점

  • 다음과 같이 간주됩니다 :
  • 일어날 것으로 예상됩니다 : 절대
  • 누가 신경 쓰는가 : 문제를 해결해야하는 사람들
  • 예 : 프로그래밍 버그, 하드웨어 오작동, 구성 실수, 파일 누락, 사용할 수없는 서버
  • 최상의 매핑 : 확인되지 않은 예외

한마디로 :

API 디자인 문제는 예외입니다. -- 그 이상도 이하도 아닌.

확인 된 예외에 대한 인수 :

확인 된 예외가 좋지 않은 이유를 이해하려면 다음과 같이 질문을하고 질문하십시오. 확인 된 예외가 언제 또는 왜 매력적인 지, 즉 컴파일러가 예외 선언을 시행하기를 원하는 이유는 무엇입니까?

때로는 : 대답은 분명 필요 예외를 잡으려고하고, 코드에 관심이있는 오류에 대한 특정 예외 클래스 이벤트를 호출되는 경우 그에만 가능합니다.

따라서, 인수 에 대한 확인 예외는 컴파일러 힘 프로그래머는 예외가 슬로우되는 선언하고 있다는 것입니다 희망 프로그래머 것이다 다음 문서 특정 예외 클래스와 그 원인 오류도.

그러나 실제로 패키지 com.acme종종 AcmeException특정 서브 클래스가 아닌 특정 서브 클래스 만 처리합니다. 그런 다음 발신자는 처리, 선언 또는 재 신호 AcmeExceptions를 보내야하지만 AcmeFileNotFoundError상황이 발생했는지 또는 확실하지 않을 수 있습니다 AcmePermissionDeniedError.

따라서에 관심이있는 AcmeFileNotFoundError경우 솔루션은 ACME 프로그래머에게 기능 요청을 제출하고의 하위 클래스를 구현, 선언 및 문서화하도록 지시하는 것입니다 AcmeException.

왜 귀찮게?

따라서 확인 된 예외가 있더라도 컴파일러는 프로그래머가 유용한 예외 를 throw하도록 할 수 없습니다 . 여전히 API 품질의 문제입니다.

결과적으로, 예외를 확인하지 않은 언어는 대개 더 나 빠지지 않습니다. 프로그래머가 Error아닌 일반 클래스 의 비 특정 인스턴스를 던지려고 할 수도 AcmeException있지만 API 품질에 관심이 있다면 AcmeFileNotFoundError결국 소개하는 법을 배우게됩니다 .

전반적으로 예외의 사양 및 문서는 일반적인 방법의 사양 및 문서와 크게 다르지 않습니다. 이것도 API 디자인 문제이며 프로그래머가 유용한 기능을 구현하거나 내보내는 것을 잊어 버린 경우 API를 유용하게 사용할 수 있도록 API를 개선해야합니다.

이러한 추론을 따르는 경우 Java와 같은 언어에서 흔히 발생하는 예외를 선언, 포착 및 다시 던지는 "번거 로움"은 거의 가치가 없다는 점이 분명합니다.

또한 Java VM에는 예외를 확인 하지 않았 으며 Java 컴파일러 만 예외를 확인하고 예외 선언이 변경된 클래스 파일은 런타임에 호환됩니다. Java VM 보안은 확인 된 예외로 개선되지 않고 코딩 스타일만으로 향상됩니다.


지난 3 년 동안 비교적 복잡한 응용 프로그램에서 여러 개발자와 협력 해 왔습니다. 적절한 오류 처리와 함께 Checked Exceptions를 자주 사용하는 코드 기반이 있고 그렇지 않은 코드베이스가 있습니다.

지금까지 Checked Exceptions가있는 코드베이스로 작업하는 것이 더 쉽다는 것을 알았습니다. 다른 사람의 API를 사용하는 경우 코드를 호출하고 기록, 표시 또는 무시하여 코드를 호출하고 올바르게 처리 할 때 어떤 종류의 오류 조건을 정확하게 볼 수 있는지 알 수 있습니다 (예, 무시하는 경우가 있습니다) ClassLoader 구현과 같은 예외). 그것은 내가 복구 할 기회를 쓰고있는 코드를 제공합니다. 모든 런타임 예외는 일반적인 오류 처리 코드로 캐시되고 처리 될 때까지 전파됩니다. 실제로 특정 수준에서 처리하고 싶지 않거나 프로그래밍 논리 오류를 고려한 확인 된 예외를 찾으면 RuntimeException에 래핑하여 버블 링합니다. 타당한 이유없이 예외를 삼키지 마십시오.

예외를 확인하지 않은 코드베이스로 작업 할 때 함수를 호출 할 때 무엇을 기대할 수 있는지 미리 알기가 조금 어렵습니다.

이것은 물론 선호도와 개발자 기술의 문제입니다. 프로그래밍과 오류 처리의 두 가지 방법 모두 똑같이 효과적이거나 효과적이지 않을 수 있으므로 One Way가 있다고 말할 수는 없습니다.

결론적으로, 특히 많은 개발자가있는 대규모 프로젝트에서 Checked Exceptions을 사용하는 것이 더 쉽다는 것을 알았습니다.


예외 카테고리

예외에 대해 이야기 할 때는 항상 Eric Lippert의 Vexing 예외 블로그 기사를 참조하십시오. 그는 다음 범주에 예외를 둡니다.

  • 치명적 -이러한 예외는 귀하의 잘못 이 아닙니다. 예방할 수 없으며 현명하게 처리 할 수 ​​없습니다. 예를 들어 OutOfMemoryError또는 ThreadAbortException.
  • 본 헤드 (Boneheaded) -이 예외 는 당신의 잘못입니다 : 당신은 그것들을 막았어야하고 그것들은 코드의 버그를 나타냅니다. 예를 들어 ArrayIndexOutOfBoundsException, NullPointerException또는 IllegalArgumentException.
  • Vexing- 이러한 예외는 예외가 아니고 , 귀하의 잘못이 아니며, 예방할 수는 없지만 처리해야합니다. 그들은 같은 던지는 종종 불행한 디자인 결정의 결과 NumberFormatException에서 Integer.parseInt대신 제공의 Integer.tryParseInt구문 분석 실패 부울 거짓을 반환하는 방법을.
  • 외생 적 – 이러한 예외 는 일반적으로 예외 가 아니며, 귀하의 잘못이 아니며, (합리적으로) 예방할 수는 없지만 처리해야합니다 . 예를 들면 다음과 같습니다 FileNotFoundException.

API 사용자 :

  • 치명적 또는 본 헤드 예외를 처리 해서는 안됩니다 .
  • vexing 예외를 처리 해야 하지만 이상적인 API에서는 발생하지 않아야합니다.
  • 외인성 예외 처리 해야합니다 .

확인 된 예외

API 사용자 특정 예외를 처리 해야 한다는 사실 은 호출자와 수신자 사이의 메소드 계약의 일부입니다. 계약은 무엇보다도, 수신자가 기대하는 인수의 수와 유형, 발신자가 기대할 수있는 반환 값의 유형, 그리고 발신자가 처리 할 것으로 예상되는 예외를 지정합니다 .

API에 vexing 예외가 없어야 하기 때문에 메소드 계약의 일부가되기 위해서는 이러한 외인성 예외 검사 해야합니다 . 상대적으로 적은 예외가 외생 적이므로 모든 API에는 상대적으로 적은 예외가 있습니다.

확인 된 예외는 처리해야하는 예외입니다 . 예외 처리는 삼키는 것만 큼 간단 할 수 있습니다. 그곳에! 예외가 처리됩니다. 기간. 개발자가 그런 식으로 처리하고 싶다면 괜찮습니다. 그러나 그는 예외를 무시할 수 없으며 경고를 받았다.

API 문제

그러나 vexing치명적인 예외 (예 : JCL)를 확인한 API는 API 사용자에게 불필요한 부담을줍니다. 이러한 예외 처리 해야 하지만, 예외는 너무 흔해서 처음에는 예외가 아니 었거나 처리 할 때 수행 할 수있는 것이 없습니다. 그리고 이로 인해 Java 개발자는 확인 된 예외를 싫어하게됩니다.

또한 많은 API에는 적절한 예외 클래스 계층 구조가 없으므로 모든 종류의 비 외생 예외 원인이 단일 검사 예외 클래스 (예 :)로 표시됩니다 IOException. 그리고 이것은 또한 Java 개발자가 확인 된 예외를 싫어하게합니다.

결론

외인성 예외는 귀하의 잘못이 아니며, 예방할 수 없었으며, 처리되어야하는 예외입니다. 이것들은 던져 질 수있는 모든 예외의 작은 부분 집합을 형성합니다. API는 외생 예외검사 하고 다른 모든 예외는 검사하지 않아야합니다. 이를 통해 API를 개선하고 API 사용자에 대한 부담을 덜어 주므로 모든 예외를 포착하고 확인되지 않은 예외를 삼키거나 다시 던질 필요가 줄어 듭니다.

따라서 Java 및 확인 된 예외를 싫어하지 마십시오. 대신, 확인 된 예외를 과도하게 사용하는 API를 싫어하십시오.


확인 된 예외는 이상적이지 않으며 약간의 경고가 있지만 목적을 제공합니다. API를 작성할 때이 API와 계약적인 특정 실패 사례가 있습니다. Java와 같이 정적으로 정적으로 유형이 지정된 언어와 관련하여 확인 된 예외를 사용하지 않는 경우 오류 가능성을 전달하기 위해 임시 문서 및 규칙에 의존해야합니다. 그렇게하면 컴파일러가 처리 오류를 가져올 수있는 모든 이점이 제거되고 프로그래머의 선의에 전적으로 의존하게됩니다.

따라서 C #에서와 같이 Checked 예외를 제거하면 오류 가능성을 프로그래밍 방식 및 구조적으로 전달할 수있는 방법은 무엇입니까? 클라이언트 코드에 이러한 오류 및 이러한 오류가 발생할 수 있고 처리해야한다고 알리는 방법은 무엇입니까?

확인 된 예외를 처리 할 때 모든 종류의 공포를 듣습니다. 오용되어 있지만 이것은 확실하지만 확인되지 않은 예외입니다. API가 많은 레이어를 쌓아 올 때 몇 년을 기다리면 실패를 전달하는 일종의 구조적 수단의 반환을 요구할 것입니다.

예외는 API 레이어의 맨 아래에 예외가 발생하고 호출 오류가 발생할 가능성이 매우 높음에도 불구 하고이 오류가 발생할 가능성을 아무도 알지 못했기 때문에 버블 링 된 경우를 가정하십시오. 던졌습니다 (예를 들어 VogonsTrashingEarthExcept ...와 달리 FileNotFoundException ...이 경우 처리 할 항목이 없기 때문에 처리 여부에 관계가 없습니다).

많은 사람들은 파일을로드 할 수없는 것이 거의 항상 프로세스의 끝이라고 말하면서 끔찍하고 고통스러운 죽음으로 죽어야한다고 주장했습니다. 그래, 그래 .. 확실히 ... 좋아 .. 당신은 무언가를위한 API를 빌드하고 어느 시점에서 파일을로드한다 ... 나는 API 사용자가 응답 할 수만있을 때 ... 프로그램이 충돌해야합니다! " 물론 예외가 발생하고 추적을 남기지 않거나 Marianna 트렌치보다 스택 추적이 깊은 EletroFlabbingChunkFluxManifoldChuggingException을 남기지 않으면 망설임없이 후자를 가져 가지만 예외를 처리하는 바람직한 방법이라는 것을 의미합니다. ? 우리는 중간에 어딘가에있을 수 없습니까? 예외는 새로운 수준의 추상화로 이동할 때마다 예외가 다시 변환되고 포장되어 실제로 무언가를 의미합니까?

마지막으로, 내가보고있는 대부분의 주장은 "예외를 다루고 싶지 않다. 많은 사람들이 예외를 다루고 싶지 않다. 확인 된 예외로 인해 예외를 처리해야하므로 확인 된 예외를 미워한다"는 것이다. 고토 지옥의 틈새로 옮기는 것은 어리석은 일이며 심판과 비전이 부족합니다.

검사 된 예외를 제거하면 함수에 대한 리턴 유형을 제거하고 항상 "anytype"변수를 리턴 할 수 있습니다. 그러면 인생이 훨씬 단순 해집니다.


실제로, 확인 된 예외는 프로그램의 견고성과 정확성을 향상시킵니다 (인터페이스의 올바른 선언을 강요해야합니다-예외는 기본적으로 특수 반환 유형입니다). 반면에 예외는 "버블 업 (bubble up)"하기 때문에 예외를 변경할 때 많은 메소드 (모든 호출자 및 호출자의 호출자 등)를 변경해야하는 문제가 있습니다. 메소드가 발생합니다.

Java에서 확인 된 예외는 후자의 문제를 해결하지 않습니다. C #과 VB.NET은 목욕물로 아기를 버리십시오.

중간 도로를 이용하는 좋은 방법은 이 OOPSLA 2005 백서 (또는 관련 기술 보고서 )에 설명되어 있습니다.

간단히 말해, 다음과 같이 말할 수 있습니다. method g(x) throws like f(x)즉, g는 f가 던지는 모든 예외를 던집니다. Voila, 계단식 변경 문제없이 예외를 확인했습니다.

학술 논문이지만 확인 된 예외의 이점과 단점이 무엇인지 설명하는 데 도움이 되므로이 문서를 읽으십시오 (일부).


Anders는 확인 된 예외의 함정과 Software Engineering 라디오의 에피소드 97 에서 C #에서 제외시킨 이유에 대해 이야기합니다 .


답변되지 않은 질문 만 해결하려면 :

예외 서브 클래스 대신 RuntimeException 서브 클래스를 처리하면 무엇을 잡아야하는지 어떻게 알 수 있습니까?

질문에는 의심스러운 추론 IMHO가 포함됩니다. API가 던지는 것을 알려주기 때문에 모든 경우에 동일한 방식으로 처리한다는 의미는 아닙니다. 다시 말해, 잡아야 할 예외는 예외를 발생시키는 구성 요소를 사용하는 상황에 따라 다릅니다.

예를 들면 다음과 같습니다.

데이터베이스에 대한 연결 테스터를 작성 중이거나 사용자의 유효성을 검사하기 위해 XPath를 입력 한 경우 작업에서 발생하는 모든 확인 및 확인되지 않은 예외를 포착하고보고하려고합니다.

그러나 처리 엔진을 작성하는 경우 NPE와 동일한 방식으로 XPathException (확인)을 처리 할 것입니다. 워커 스레드의 맨 위로 실행되도록하고 나머지 배치를 건너 뛰고 로그하십시오. 문제를 진단하거나 진단을 위해 지원 부서에 보냅니다.


문제

예외 처리 메커니즘에서 볼 수있는 최악의 문제 는 코드 복제가 대규모 로 발생한다는 것입니다 ! 솔직하게 말하자 : 대부분의 프로젝트에서 95 %의 시간 동안 개발자가 실제로 예외적으로해야하는 것은 사용자에게 (어떤 경우에는 개발 팀에게 e) 스택 추적이있는 메일). 따라서 일반적으로 예외가 처리되는 모든 장소에서 동일한 코드 / 블록이 사용됩니다.

확인 된 예외 유형에 대해 각 catch 블록에 간단한 로깅을 수행한다고 가정 해 봅시다.

try{
   methodDeclaringCheckedException();
}catch(CheckedException e){
   logger.error(e);
}

일반적인 예외라면 더 큰 코드베이스에 수백 개의 try-catch 블록이있을 수 있습니다. 이제 콘솔 로깅 대신 팝업 대화 상자 기반 예외 처리를 도입하거나 추가로 개발 팀에 전자 메일을 보내야한다고 가정 해 봅시다.

잠깐만 ... 코드에서 수백 개의 위치를 ​​모두 편집할까요?! 당신은 내 요점을 얻는다 :-).

해결책

이 문제를 해결 하기 위해 수행 한 것은 예외 처리 중앙 집중화 하기 위해 예외 처리기 (EH라고도 함) 개념을 도입하는 것이 었습니다 . 예외를 처리해야하는 모든 클래스에는 예외 처리기 인스턴스가 의존성 주입 프레임 워크에 의해 주입됩니다 . 따라서 일반적인 예외 처리 패턴은 다음과 같습니다.

try{
    methodDeclaringCheckedException();
}catch(CheckedException e){
    exceptionHandler.handleError(e);
}

예외 처리를 사용자 정의하려면 코드를 한 곳에서 변경하면됩니다 (EH 코드).

물론 더 복잡한 경우에 우리는 EH의 여러 서브 클래스를 구현하고 DI 프레임 워크가 제공하는 기능을 활용할 수 있습니다. DI 프레임 워크 구성을 변경하여 EH 구현을 전체적으로 쉽게 전환하거나 특별한 예외 처리 요구가있는 클래스 (예 : Guice @Named 주석 사용)가있는 특정 EH 구현을 클래스에 제공 할 수 있습니다.

이렇게하면 개발 및 릴리스 버전의 응용 프로그램에서 예외 처리 동작을 차별화 할 수 있습니다 (예 : 개발-오류 로깅 및 응용 프로그램 중지, prod-오류를 자세한 내용으로 로깅 및 응용 프로그램 실행 계속).

마지막으로

마지막으로 예외를 최상위 레벨 예외 처리 클래스에 도달 할 때까지 예외를 "위로"전달하면 동일한 유형의 중앙 집중화를 얻을 수 있습니다. 그러나 그로 인해 코드가 복잡해지고 메소드의 서명이 생성 되고이 스레드에서 다른 사람들이 언급 한 유지 관리 문제가 발생합니다.


사람들이 이미 언급했듯이 검사 된 예외는 Java 바이트 코드에 존재하지 않습니다. 다른 구문 검사와 달리 단순히 컴파일러 메커니즘입니다. 컴파일러가 중복 조건부에 대해 불평하는 것처럼 확인 된 예외를 많이 봅니다 if(true) { a; } b;. 도움이 되긴하지만 의도적으로이 작업을 수행했을 수도 있으므로 경고를 무시하겠습니다.

문제의 사실은, 당신이 확인 된 예외를 강제하고 다른 모든 사람들이 당신이 만든 규칙 때문에 당신을 미워하는 담보 손해라면 모든 프로그래머가 "올바른 일을"하도록 강요 할 수 없다는 것입니다.

나쁜 프로그램을 수정하십시오! 언어를 허용하지 않도록 언어를 수정하려고하지 마십시오! 대부분의 사람들에게 "예외에 대해 무언가를하는 것"은 실제로 사용자에게 예외를 알려주는 것입니다. 확인되지 않은 예외에 대해서도 사용자에게 알릴 수 있으므로 확인 된 예외 클래스를 API에서 제외하십시오.


c2.com 에 대한 저의 글은 여전히 ​​원래 형식과 거의 동일합니다. CheckedExceptionsAreIncompatibleWithVisitorPattern

요약해서 말하자면:

방문자 패턴과 그 친척은 간접 호출자와 인터페이스 구현이 예외에 대해 알고 있지만 인터페이스와 직접 호출자가 알 수없는 라이브러리를 형성하는 인터페이스 클래스입니다.

CheckedExceptions의 기본 가정은 선언 된 모든 예외는 해당 선언으로 메소드를 호출하는 모든 지점에서 발생할 수 있다는 것입니다. VisitorPattern은이 가정이 잘못되었음을 밝힙니다.

이와 같은 경우에 확인 된 예외의 최종 결과는 런타임에 컴파일러의 확인 된 예외 제약 조건을 본질적으로 제거하는 많은 쓸모없는 코드입니다.

근본적인 문제에 관해서는 :

내 일반적인 아이디어는 최상위 처리기가 예외를 해석하고 적절한 오류 메시지를 표시해야한다는 것입니다. 나는 거의 항상 IO 예외, 통신 예외 (어떤 이유로 API가 구별) 또는 작업 치명적 오류 (백업 서버의 프로그램 버그 또는 심각한 문제)를 보이므로 스택 추적을 심각하게 허용하면 너무 어렵지 않아야합니다 서버 문제.


이 기사 는 내가 읽은 Java의 예외 처리에 관한 최고의 텍스트입니다.

검사되지 않은 예외보다 검사되지 않은 것을 선호하지만이 선택은 매우 격렬하고 강력한 논거를 기반으로 설명됩니다.

여기에 기사 내용을 너무 많이 인용하고 싶지는 않지만 (전체로 읽는 것이 가장 좋습니다)이 스레드에서 확인되지 않은 예외 옹호자의 주장 대부분을 다루고 있습니다. 특히이 논쟁 (꽤 인기있는 것으로 보입니다)은 다음과 같습니다.

예외는 API 레이어의 맨 아래에 예외가 발생하고 호출 오류가 발생할 가능성이 매우 높음에도 불구 하고이 오류가 발생할 가능성을 아무도 알지 못했기 때문에 버블 링 된 경우를 가정하십시오. 던졌습니다 (예를 들어 VogonsTrashingEarthExcept ...와 달리 FileNotFoundException ...이 경우 처리 할 항목이 없기 때문에 처리 여부에 관계가 없습니다).

저자는 "응답":

모든 런타임 예외를 잡아서 응용 프로그램의 "최상위"로 전파 할 수 없다고 가정하는 것은 절대적으로 올바르지 않습니다. (...) 시스템 / 비즈니스 요구 사항에 따라 명확하게 처리해야하는 모든 예외적 인 조건에 대해 프로그래머는 조건을 파악한 후 조건을 파악한 후 수행 할 작업을 결정해야합니다. 이는 컴파일러 경고를 기반으로하지 않고 응용 프로그램의 실제 요구에 따라 엄격하게 수행해야합니다. 다른 모든 오류는 오류가 기록 될 최상위 처리기로 자유롭게 전파 될 수 있어야하며 적절한 조치 (아마도 종료)가 수행됩니다.

그리고 주요 생각이나 기사는 다음과 같습니다.

소프트웨어의 오류 처리와 관련하여 유일하게 안전하고 정확한 가정은 존재하는 모든 서브 루틴 또는 모듈에서 오류가 발생할 수 있다는 것입니다!

" 아무도이 오류가 발생할 가능성을 알지 못하면" 해당 프로젝트에 문제가있는 것입니다. 이러한 예외는 최소한 가장 일반적인 예외 처리기 (예 :보다 구체적인 처리기에서 처리하지 않는 모든 예외를 처리하는 예외 처리기)에 의해 처리되어야합니다.

슬프게도 많은 사람들이이 위대한 기사를 발견하는 것 같지 않습니다.


확인 된 예외는 원래 형태로 실패가 아닌 비상 사태를 처리하려는 시도였습니다. 주목할만한 목표는 특정 예측 가능한 포인트 (연결할 수 없음, 파일을 찾을 수 없음 등)를 강조하고 개발자가 이러한 점을 처리하도록하는 것이 었습니다.

원래 개념에 포함되지 않은 것은 광범위한 체계적 및 복구 불가능한 오류를 선언하는 것이 었습니다. 이러한 실패는 확인 된 예외로 선언되지 않았습니다.

일반적으로 코드에서 장애가 발생할 수 있으며 EJB, 웹 및 Swing / AWT 컨테이너는 가장 외부의 "실패한 요청"예외 처리기를 제공하여 이미이를 충족시킵니다. 가장 기본적인 올바른 전략은 트랜잭션을 롤백하고 오류를 반환하는 것입니다.

한 가지 중요한 점은 런타임 및 검사 예외가 기능적으로 동일하다는 것입니다. 점검 된 예외가 수행 할 수있는 처리 또는 복구가 없으며 런타임 예외는 수행 할 수 없습니다.

"체크 된"예외에 대한 가장 큰 주장은 대부분의 예외를 수정할 수 없다는 것입니다. 간단한 사실은 우리가 파산 한 코드 / 서브 시스템을 소유하지 않는다는 것입니다. 우리는 구현을 볼 수 없으며 책임이 없으며 해결할 수 없습니다.

애플리케이션이 DB가 아닌 경우 DB를 수정하려고 시도해서는 안됩니다. 그것은 캡슐화원칙을 위반할 것 입니다.

특히 문제는 JDBC (SQLException)와 EJB를위한 RMI (RemoteException) 영역이었습니다. 원래의 "체크 된 예외"개념에 따라 고정 가능한 우발성을 식별하는 대신, 실제로 고칠 수없는 이러한 광범위한 시스템 신뢰성 문제는 널리 선언되어야합니다.

Java 디자인의 또 다른 심각한 결함은 예외 처리가 가능한 가장 높은 "비즈니스"또는 "요청"레벨에 올바르게 배치되어야한다는 것입니다. 여기서의 원칙은 "빨리 던져서 늦게 잡아라"입니다. 확인 된 예외는 거의 수행하지 않지만 방해가됩니다.

우리는 자바에서 수천 개의 do-nothing try-catch 블록을 요구하는 명백한 문제가 있으며, 상당 부분 (40 % +)이 잘못 코딩되었습니다. 이들 중 어느 것도 진정한 처리 또는 신뢰성을 구현하지는 않지만 주요 코딩 오버 헤드를 부과합니다.

마지막으로 "체크 된 예외"는 FP 기능 프로그래밍과 거의 호환되지 않습니다.

"즉시 처리"에 대한 그들의 주장은 "캐치 늦게"예외 처리 모범 사례와 루프 / 또는 제어 흐름을 추상화하는 FP 구조와 상충됩니다.

많은 사람들이 "처리 된"예외에 대해 이야기하지만 모자를 통해 이야기하고 있습니다. 성공 가장 하기 위해 널 (null), 불완전하거나 잘못된 데이터로 실패한 후에도 계속 처리하는 것은 아무 것도 처리하지 않습니다. 가장 낮은 형태의 엔지니어링 / 신뢰성 과실입니다.

깨끗하게 실패하면 예외를 처리하기위한 가장 기본적인 올바른 전략입니다. 트랜잭션을 롤백하고 오류를 기록하고 사용자에게 "실패"응답을보고하는 것은 올바른 연습이며, 가장 중요한 것은 데이터베이스에 잘못된 비즈니스 데이터가 커밋되지 않도록 방지하는 것입니다.

예외 처리를위한 다른 전략은 비즈니스, 하위 시스템 또는 요청 수준에서 "다시 시도", "다시 연결"또는 "건너 뛰기"입니다. 이 모든 것은 일반적인 안정성 전략이며 런타임 예외와 함께 잘 작동합니다.

마지막으로, 잘못된 데이터로 실행하는 것보다 실패하는 것이 훨씬 바람직합니다. 계속하면 원래 원인과 거리가 멀고 디버그하기 어려운 2 차 오류가 발생합니다. 또는 결과적으로 잘못된 데이터가 커밋됩니다. 사람들은 해고 당합니다.

참조 :
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/


이것은 점검 된 예외의 순수한 개념에 대한 논쟁은 아니지만 Java가 사용하는 클래스 계층 구조는 괴물 쇼입니다. 우리는 항상 "예외 (exceptions)"라고 부릅니다 . 언어 사양 에서도 그것을 호출 하기 때문에 정확합니다. 그러나 유형 시스템에서 예외의 이름을 어떻게 나타내고 표현합니까?

수업으로 Exception상상 하는가? 글쎄, Exceptions는 예외이고, 마찬가지로 예외는 Exceptions가 아닌 예외를 제외하고는 Exception다른 예외는 실제로는 다른 예외이기 때문에 예외가 아닌 예외는 Error예외입니다. 때, 그리고 때로는 당신이해야 할 경우를 제외하고는 결코 잡을 수 없습니다. 당신은 또한도 다른 예외 정의 할 수 있기 때문에 그게 전부가 아니다 제외 Exception의도 Error의하지만 단지 Throwable예외.

다음 중 "확인 된"예외는 무엇입니까? Throwable들 그들은 또한 인 경우 제외시켰다 예외를 체크 Error되지 않은 예외되는 s가하고있다 Exception또한의, Throwable하나 개의 예외도 그에게 거기를 제외하고, S 및 확인이 끝난 예외의 주요 유형을 것입니다있는 경우 그들은 또한 RuntimeException그 체크되지 않는 예외의 다른 종류이기 때문에,이야.

무엇입니까 RuntimeException? 이름에서 알 수 있듯이 모든 예외와 같은 예외이며 Exception실제로 모든 예외와 마찬가지로 런타임에서 발생합니다. 단, RuntimeException예외가 발생 Exception하지 않아야하기 때문에 다른 런타임과 비교하여 예외적 이라는 점을 제외하고 는 예외입니다. 당신이 바보 같은 실수를 할 때, RuntimeExceptions는 결코 Errors가 아니기 때문에 예외적으로 잘못되었지만 실제로는 그렇지 않은 것들을위한 것입니다 Error. for RuntimeErrorException제외하고 실제로는 RuntimeExceptionfor Error입니다. 그러나 모든 예외가 잘못된 상황을 나타내는 것은 아닙니다. 그렇습니다. ThreadDeath문서에서 "정상 발생"이라고 설명하고 예외가 아닌 예외를 제외하고 는 예외입니다.Error

어쨌든, 모든 예외를 중간에서 Errors (예외 실행 예외의 경우에는 검사되지 않음)와 Exceptions (예외 실행 오류가 적은 경우 , 그렇지 않은 경우를 제외하고 검사 됨) 로 나누기 때문에 이제 두 개가 필요합니다. 여러 예외 각각의 다른 종류. 따라서 우리는 IllegalAccessErrorand IllegalAccessException, and InstantiationErrorand InstantiationException, and NoSuchFieldErrorand NoSuchFieldException, and NoSuchMethodErrorand NoSuchMethodException, and ZipErrorand가 필요 ZipException합니다.

예외가 점검 되더라도 컴파일러를 부정하고 점검하지 않고 던질 수있는 방법은 항상 있습니다. 당신이 할 경우, 당신은 얻을 수 UndeclaredThrowableException그것이로 던질 수있는 다른 사례를 제외하고 UnexpectedException, 또는 UnknownException(관련이있는 UnknownError경우에만 "심각한 예외"에 대한 인), 또는 ExecutionException, 또는 InvocationTargetException, 또는 ExceptionInInitializerError.

아, 그리고 우리는 8의 새로운 세련되지 자바를 잊지 말아야 UncheckedIOExceptionRuntimeException검사는 포장하여 창 밖으로 개념을 확인 예외를 던질 수 있도록 설계 예외 IOExceptionI에 의한 예외 / 일으키지 않는 O 오류 ( IOError즉 존재하지만 예외 또한) 처리하기가 매우 어려우므로 확인하지 않아도됩니다.

감사합니다 Java!


확인 된 예외의 문제점은 인터페이스의 한 구현조차도 예외를 사용하는 경우 인터페이스의 메소드에 예외가 종종 첨부된다는 것입니다.

확인 된 예외의 또 다른 문제점은 오용되는 경향이 있다는 것입니다. 이것의 완벽한 예는 java.sql.Connectionclose()방법입니다. Connection이 완료 되었다고SQLException 이미 명시 적으로 언급 했지만을 던질 수 있습니다 . 어떤 정보가 close ()에 관심을 가질 수 있는지 전달할 수 있습니까?

일반적으로 connection을 닫을 때 *다음과 같이 보입니다.

try {
    conn.close();
} catch (SQLException ex) {
    // Do nothing
}

또한 다양한 구문 분석 방법과 NumberFormatException에서 시작하지 마십시오 ... 예외를 throw하지 않는 .NET의 TryParse는 사용하기가 훨씬 쉽습니다 .Java로 돌아가는 것이 고통 스럽습니다 (Java와 Java를 모두 사용합니다) 내가 일하는 C #).

*추가 설명으로 PooledConnection의 Connection.close ()는 연결을 닫지 않지만 확인 된 예외이기 때문에 여전히 SQLException을 잡아야합니다.


다음은 확인 된 예외에 대한 한 가지 주장입니다 (joelonsoftware.com).

추론은 1960 년대 이후로 유해한 것으로 간주되는 "고토 (goto 's)"보다 예외는 예외적 인 것으로 간주하여 한 코드 포인트에서 다른 코드 포인트로 갑작스런 점프를하기 때문입니다. 실제로 그들은 goto보다 훨씬 나쁩니다.

  • 그들은 소스 코드에서 보이지 않습니다. 예외를 던지거나 던지지 않을 수있는 함수를 포함하여 코드 블록을 살펴보면 어떤 예외가 어디서 발생하는지 확인할 수있는 방법이 없습니다. 이것은 신중한 코드 검사조차도 잠재적 인 버그를 나타내지 않음을 의미합니다.
  • 함수에 대해 가능한 종료 점이 너무 많습니다. 올바른 코드를 작성하려면 함수를 통해 가능한 모든 코드 경로를 고려해야합니다. 예외를 발생시킬 수있는 함수를 호출하고 그 자리에서이를 포착하지 못할 때마다 갑자기 종료되어 함수가 일관성이없는 상태 또는 다른 코드 경로에 있지 않은 상태로 인해 발생하는 깜짝 버그가 발생할 가능성이 있습니다. 생각 해봐

프로그래머 는 메소드를 올바르게 사용하기 위해 메소드에서 발생할 수있는 모든 예외 를 알아야 합니다. 따라서 예외 중 일부만으로 머리 위로 때리는 것이 부주의 한 프로그래머가 오류를 피하는 데 도움이되는 것은 아닙니다.

슬림 한 이점은 부담이 큰 비용보다 중요합니다 (특히 인터페이스 서명을 지속적으로 수정하는 것이 실용적이지 않은 더 크고 유연하지 않은 코드 기반의 경우).

정적 분석은 훌륭하지만 신뢰할 수있는 정적 분석은 종종 프로그래머의 엄격한 작업을 유연하게 요구합니다. 비용-편익 계산이 있으며 컴파일 시간 오류로 이어지는 검사를 위해 막대를 높게 설정해야합니다. IDE가 메소드가 던질 수있는 예외 (피할 수없는 예외 포함)를 전달하는 역할을 수행하는 것이 더 도움이됩니다. 강제 예외 선언 없이는 신뢰할 수 없지만 대부분의 예외는 여전히 문서에 선언되어 있으며 IDE 경고의 신뢰성은 그다지 중요하지 않습니다.


나는 이것이 훌륭한 질문이며 전혀 논쟁의 여지가 없다고 생각합니다. 타사 라이브러리는 일반적으로 검사되지 않은 예외를 throw해야한다고 생각합니다 . 이것은 라이브러리에 대한 의존성을 격리시킬 수 있음을 의미합니다 (즉, 예외를 다시 던지거나 던질 필요가 없습니다 Exception-일반적으로 나쁜 습관). Spring의 DAO 레이어 가 이에 대한 훌륭한 예입니다.

할 수있을 경우, 다른 한편으로는, 핵심에서 예외는 자바 API는 일반 점검해야한다 이제까지 처리 할 수. FileNotFoundException또는 (나의 마음에 드는) 가져 가라 InterruptedException. 이러한 조건은 거의 항상 구체적으로 처리 해야합니다 (즉,에 대한 반응이에 대한 반응 InterruptedException과 같지 않음 IllegalArgumentException). 예외를 확인한다는 사실 때문에 개발자는 조건을 처리 할 수 ​​있는지 여부를 생각해야합니다. (저는 InterruptedException제대로 처리하는 것을 거의 보지 못했습니다 !)

한가지 더 – a RuntimeException가 항상 "개발자가 뭔가 잘못한 것"은 아닙니다. enum사용 을 시도 할 때 잘못된 인수 예외가 발생 하며 해당 이름 valueOf이 없습니다 enum. 개발자가 반드시 실수는 아닙니다!


확인 된 예외가 필요하지 않다는 증거는 다음과 같습니다.

  1. Java에서 작동하는 많은 프레임 워크. JDBC 예외를 검사되지 않은 예외로 랩핑하여 메시지를 로그에 던지는 Spring처럼
  2. Java 플랫폼 에서조차도 Java 다음에 오는 많은 언어-사용하지 않습니다.
  3. 확인 된 예외는 클라이언트가 예외를 throw하는 코드를 사용하는 방법에 대한 친절한 예측입니다. 그러나이 코드를 작성하는 개발자는 코드 클라이언트가 작업중인 시스템 및 비즈니스에 대해 전혀 알지 못합니다. 예를 들어 체크 된 예외를 강제로 발생시키는 Interfcace 메소드. 시스템에 100 개의 구현이 있으며 50 개 또는 90 개의 구현이이 예외를 발생시키지 않지만 클라이언트가 해당 인터페이스를 참조하는 경우 클라이언트는 여전히이 예외를 포착해야합니다. 이러한 50 또는 90 구현은 예외를 로그에 넣는 자체 예외를 처리하는 경향이 있습니다 (이것은 좋은 동작입니다). 우리가 어떻게해야합니까? 로그에 메시지를 보내는 모든 작업을 수행하는 백그라운드 로직이 더 좋습니다. 그리고 코드 클라이언트 인 경우 예외 처리가 필요하다고 생각되면 처리하겠습니다.
  4. Java에서 I / O로 작업 할 때의 또 다른 예는 파일이 존재하지 않으면 모든 예외를 확인하도록 강요합니까? 내가 그걸로 무엇을해야합니까? 존재하지 않으면 시스템은 다음 단계로 진행되지 않습니다. 이 메소드의 클라이언트는 해당 파일에서 예상되는 컨텐츠를 얻지 못합니다. 런타임 예외를 처리 할 수 ​​있습니다. 그렇지 않으면 먼저 확인 된 예외를 확인하고 로그에 메시지를 넣은 다음 메소드에서 예외를 버려야합니다. 아니요 ... 아니요-RuntimeEception으로 자동으로 수행하는 것이 좋습니다. 자동으로 켜집니다. 수동으로 처리하는 것은 의미가 없습니다. 로그에 오류 메시지가 표시되어 기쁩니다 (AOP가 도움이 될 수 있습니다 .. java를 수정하는 것). 결국 시스템이 최종 사용자에게 팝업 메시지를 표시해야한다고 결정하면 문제가 아니라 표시합니다.

Java가 I / O와 같은 코어 라이브러리로 작업 할 때 사용할 항목을 선택할 수 있다면 기뻤습니다 . Like는 RuntimeEception으로 랩핑 된 동일한 클래스의 두 사본을 제공합니다. 그러면 사람들이 무엇을 사용할지 비교할 수 있습니다 . 그러나 지금은 많은 사람들이 Java 또는 다른 언어에서 일부 프레임 워크를 사용하는 것이 좋습니다. 스칼라처럼 JRuby도 마찬가지입니다. 많은 사람들은 SUN이 옳았다 고 믿습니다.


아무도 언급하지 않은 중요한 것은 인터페이스와 람다 식을 방해하는 방법입니다.

을 정의한다고 가정 해 보겠습니다 MyAppException extends Exception. 응용 프로그램에서 발생한 모든 예외에서 상속 된 최상위 예외입니다. 모든 방법은 throws MyAppException어느 것이 약간 불쾌하지만 관리 가능하다고 선언합니다 . 예외 처리기는 예외를 기록하고 어떻게 든 사용자에게 알립니다.

귀하의 인터페이스가 아닌 일부 인터페이스를 구현할 때까지 모두 괜찮아 보입니다. 분명히 throw 의도를 선언하지 않으므로 MyApException컴파일러는 예외를 throw 할 수 없습니다.

그러나 예외가 확장 RuntimeException되면 인터페이스에 문제가 없습니다. 원하는 경우 JavaDoc에서 예외를 자발적으로 언급 할 수 있습니다. 그러나 그 외에는 예외 처리 계층에 잡히기 위해 자동으로 모든 것을 통해 버블 링합니다.


우리는 C #의 수석 설계자에 대한 언급을 보았습니다.

다음은 확인 된 예외를 언제 사용해야하는지에 대한 Java 담당자의 다른 관점입니다. 그는 다른 사람들이 언급 한 많은 부정적인 점을 인정합니다 : 효과적인 예외


나는 예외 처리에 대해 많이 읽었지만 (대부분의 경우) 실제로 확인 된 예외의 존재에 대해 행복하거나 슬프다 고 말할 수는 없습니다. 낮은 수준의 코드에서 확인 된 예외 (IO, 네트워킹 , OS 등) 및 상위 수준 API / 응용 프로그램 수준에서 확인되지 않은 예외.

그들 사이에 선을 그리는 것이 쉽지 않더라도 확인 된 예외를 많이 포장하지 않고 동일한 지붕 아래 여러 API / 라이브러리를 통합하는 것은 실제로 성가시다 / 어려움입니다. 일부 예외를 잡아서 현재 상황에서 더 의미있는 다른 예외를 제공하는 것이 유용합니다.

내가 작업하고있는 프로젝트는 많은 라이브러리를 가져 와서 동일한 API, 확인되지 않은 예외를 완전히 기반으로하는 API로 통합합니다.이 프레임 워크는 처음에는 확인 된 예외로 가득 차 있고 확인되지 않은 몇 가지만있는 고급 API를 제공합니다 예외 (초기화 예외, ConfigurationException 등)는 매우 친절 하지 않다고 말해야합니다 . 대부분의 경우 처리 방법을 모르거나 예외를 무시하지 않아야하는 예외를 포착하거나 다시 발생시켜야했습니다 (특히 혼동하지 말아야합니다). 클릭하면 10 가지 (확인 된) 예외가 발생할 수 있습니다.

현재 버전 (3 번째)은 확인되지 않은 예외 만 사용하며, 포착되지 않은 모든 것을 처리하는 전역 예외 처리기가 있습니다. API는 예외 처리기를 등록하는 방법을 제공합니다. 예외 처리기는 예외로 판단되는 경우 (대부분의 경우) 누군가에게 로그 및 알림을 의미하거나 다른 예외를 의미 할 수 있습니다.이 예외와 같은 AbortException, 이것은 현재 실행 스레드를 중단하고 원하지 않는 오류를 기록하지 않음을 의미합니다. 물론 모든 사용자 정의 스레드를 해결하려면 try {...} catch (all)로 run () 메서드를 처리해야합니다.

공공 무효 실행 () {

try {
     ... do something ...
} catch (Throwable throwable) {
     ApplicationContext.getExceptionService().handleException("Handle this exception", throwable);
}

}

WorkerService를 사용하여 모든 작업을 처리하는 작업 (Runnable, Callable, Worker)을 예약하는 경우에는 필요하지 않습니다.

물론 이것은 단지 내 의견 일 뿐이며 그것이 옳지 않을 수도 있지만 그것은 나에게 좋은 접근 방법처럼 보입니다. 나는 그것이 나에게 좋다고 생각하는 것이 다른 사람들에게도 좋다면 프로젝트를 발표 한 후에 볼 것이다. :)

참고 URL : https://stackoverflow.com/questions/613954/the-case-against-checked-exceptions



도와주세요.
반응형