체크된 예외에 대한 사례
몇 년 동안 다음과 같은 질문에 대한 적절한 답변을 얻지 못했습니다.왜 일부 개발자들은 체크된 예외에 그렇게 반대합니까?저는 수많은 대화를 나누었고, 블로그에 있는 것들을 읽었으며, 브루스 에클이 말하는 것을 읽었습니다.
현재 새로운 코드를 작성하고 있으며, 예외에 대한 대응에 매우 주의를 기울이고 있습니다."확인된 예외를 좋아하지 않는다"는 군중들의 관점을 알아보려고 노력 중인데 아직도 보이지 않는다.
내가 하는 모든 대화는 같은 질문의 답이 없는 것으로 끝나.셋업하겠습니다.
일반적으로 (Java의 설계 방법에 따라)
Error
절대 잡히지 않는 것(VM에 땅콩 알레르기가 있어 누군가가 땅콩 병을 떨어뜨린 경우)RuntimeException
프로그래머가 잘못한 것(프로그래머가 배열 끝에서 벗어남)에 대한 것입니다.Exception
(제외)RuntimeException
프로그래머가 제어할 수 없는 것(파일 시스템에 쓰는 동안 디스크가 꽉 차서 프로세스의 파일 핸들 제한에 도달하여 더 이상 파일을 열 수 없음)을 말합니다.Throwable
는 모든 예외 유형의 부모일 뿐입니다.
일반적인 주장은 예외가 발생하면 개발자는 프로그램을 종료하기만 하면 된다는 것입니다.
또 다른 일반적인 주장은 체크된 예외가 코드를 리팩터링하는 것을 더 어렵게 만든다는 것입니다.
"끝내기만 하면 됩니다" 인수의 경우 종료하는 경우에도 적절한 오류 메시지가 표시되어야 합니다.에러 처리에만 몰두하고 있는 경우는, 그 이유를 명확하게 나타내지 않고 프로그램을 종료해도, 유저는 그다지 기뻐하지 않습니다.
"리팩터링하기 어렵게 만든다"는 군중은 적절한 수준의 추상화가 선택되지 않았음을 나타냅니다.선언하는 것이 아니라, 메서드에 의해서IOException
,그IOException
현재 진행 중인 상황에 더 적합한 예외로 변환해야 합니다.
Main을 포장하는 데 아무런 문제가 없습니다.catch(Exception)
(또는 경우에 따라서는catch(Throwable)
프로그램을 정상적으로 종료할 수 있도록 하기 위해 필요한 특정 예외를 항상 파악합니다.이를 통해 최소한 적절한 오류 메시지를 표시할 수 있습니다.
사람들이 대답하지 않는 질문은 다음과 같습니다.
던지면
RuntimeException
대신 서브클래스Exception
서브클래스에서는 무엇을 잡아야 하는지 어떻게 알 수 있습니까?
만약 답이 잡히면Exception
시스템 예외와 같은 방법으로 프로그래머 오류를 처리합니다.그건 잘못된 것 같아요.
잡으면Throwable
시스템 예외 및 VM 오류(등)를 동일하게 처리합니다.그건 잘못된 것 같아요.
만약 당신이 던져진 예외만을 잡는다면, 당신은 어떤 예외들이 던져지는지 어떻게 알 수 있나요?프로그래머 X가 새로운 예외를 던지고 이를 포착하는 것을 잊으면 어떻게 됩니까?그것은 나에게 매우 위험해 보인다.
스택 트레이스를 표시하는 프로그램이 잘못되었다고 생각합니다.체크된 예외를 좋아하지 않는 사람은 그렇게 느끼지 않는가?
체크된 예외가 마음에 들지 않으면 이유를 설명하고 답변이 없는 질문에 답해 주시겠습니까?
어느 모델을 사용할 것인가에 대한 조언은 필요 없습니다.제가 찾고 있는 것은 왜 사람들이 이 모델을 사용할 것인가 하는 것입니다.RuntimeException
왜냐하면 그들은 연장하는 것을 싫어하기 때문이다.Exception
또한/또는 예외를 포착한 후 재투입하는 이유RuntimeException
그들의 방식에 스로우(throw)를 더하는 것입니다.체크된 예외를 싫어하는 동기를 알고 싶다.
나는 당신이 했던 것과 같은 브루스 에켈 인터뷰를 읽은 것 같다. 그리고 그것은 항상 나를 괴롭힌다.실제로 이 논쟁은 MS의 천재 앤더스 헤일스버그 인터뷰 대상자에 의해 이뤄졌다.NET 및 C#.
비록 나는 헤일스버그와 그의 작품을 좋아하지만, 이 주장은 항상 나를 가짜로 여겨왔다.기본적으로 다음과 같이 요약됩니다.
"체크된 예외는 프로그래머가 이를 항상 포착하고 무시함으로써 악용하기 때문에 좋지 않습니다.그렇게 하지 않으면 사용자에게 제시될 수 있는 문제가 숨겨지고 무시됩니다."
"다른 방법으로 사용자에게 표시"라는 것은 런타임 예외를 사용하면 게으른 프로그래머가 이를 무시(빈 캐치 블록으로 캐치하는 것에 비해)하고 사용자가 이를 볼 수 있다는 것을 의미합니다.
이 주장의 요약은 "프로그래머가 제대로 사용하지 않고 제대로 사용하지 않는 것이 없는 것보다 더 나쁘다"는 것이다.
이 주장에는 어느 정도 진실이 있습니다.사실 Goslings가 Java에 연산자 오버라이드를 넣지 않은 동기는 비슷한 주장에서 기인한다고 생각합니다.그것들은 종종 악용되기 때문에 프로그래머를 혼란스럽게 합니다.
하지만 결국, 나는 그것이 헤일스버그의 거짓된 주장이고 아마도 잘 생각해낸 결정이라기 보다는 부족함을 설명하기 위해 만들어진 포스트혹의 주장이라는 것을 알게 되었다.
체크된 예외를 과도하게 사용하는 것은 나쁘고 사용자의 허술한 취급을 초래하는 경향이 있지만, 이를 적절히 사용하는 것은 API 클라이언트 프로그래머에게 큰 이익을 가져다 줄 수 있습니다.
API 프로그래머는 체크된 예외를 곳곳에 던지지 않도록 주의해야 합니다.그렇지 않으면 클라이언트 프로그래머를 귀찮게 할 뿐입니다.매우 게으른 클라이언트 프로그래머는 캐치볼에 의지할 것이다.(Exception) {}
Hejlsberg가 경고했듯이 모든 이익은 사라지고 지옥이 뒤따를 것이다.그러나 경우에 따라서는 제대로 체크된 예외를 대체할 수 있는 방법이 없습니다.
저에게 전형적인 예는 파일 오픈 API입니다.(적어도 파일시스템상의) 언어 역사 내의 모든 프로그래밍 언어에는 파일을 열 수 있는 API가 있습니다.그리고 이 API를 사용하는 모든 클라이언트 프로그래머는 열려고 하는 파일이 존재하지 않는 경우를 처리해야 한다는 것을 알고 있습니다.바꿔 말하면, 이 API를 사용하는 모든 클라이언트 프로그래머는 이 케이스에 대처해야 한다는 것을 알아야 합니다.문제는 API 프로그래머가 코멘트만으로 대처해야 한다는 것을 알 수 있도록 도와줄 수 있는가, 아니면 클라이언트가 대처해야 한다고 주장할 수 있는가 하는 것이다.
C에서 사자성어는 다음과 같다.
if (f = fopen("goodluckfindingthisfile")) { ... }
else { // file not found ...
어디에fopen
0을 반환하고 C를 반환함으로써 장애를 나타냅니다(바보리하게).이 경우 0을 부울로 취급할 수 있습니다.기본적으로 이 사자성어를 배우면 괜찮아요.하지만 만약 당신이 시골뜨기이고 사자성어를 배우지 못했다면요?그럼, 물론, 당신은 먼저
f = fopen("goodluckfindingthisfile");
f.read(); // BANG!
열심히 배우도록 하겠습니다.
여기서는 강력한 유형의 언어만 언급하고 있습니다.API가 어떤 언어인지 알기 쉽게 알 수 있습니다.각 프로토콜에 대해 명확하게 정의된 프로토콜과 함께 사용할 수 있는 기능(메서드)의 Smorgasbord입니다.
명확하게 정의된 프로토콜은 일반적으로 메서드 서명에 의해 정의됩니다.여기서 fopen에서는 문자열(또는 C의 경우 문자*)을 전달해야 합니다.다른 것을 지정하면 컴파일 시간 오류가 발생합니다.프로토콜을 따르지 않았습니다. API를 제대로 사용하지 않았습니다.
일부 언어에서는 반환 유형도 프로토콜의 일부입니다.에 상당하는 콜을 시도하면fopen()
변수에 할당하지 않은 일부 언어에서는 컴파일 시간 오류도 발생합니다(void 함수에서만 가능합니다).
제가 말하고자 하는 요점은 다음과 같습니다.정적으로 입력된 언어에서 API 프로그래머는 클라이언트 코드가 명백한 실수를 했을 때 컴파일되지 않도록 함으로써 클라이언트가 API를 적절하게 사용하도록 장려합니다.
(Ruby와 같이 동적으로 입력된 언어에서는 파일명으로 float라고 하는 모든 것을 전달할 수 있습니다.그러면 컴파일 됩니다.메서드 인수를 제어하지 않을 경우 사용자가 선택한 예외로 인해 번거로운 작업을 수행할 필요가 있습니다.여기서 설명하는 인수는 정적으로 입력된 언어에만 적용됩니다.)
그러면 체크된 예외는 어떻게 되나요?
여기 파일을 열 때 사용할 수 있는 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
주의:FileNotFoundException
는 체크된 예외입니다.
API 프로그래머는 다음과 같이 말합니다. "이 컨스트럭터를 사용하여 새로운 FileInputStream을 만들 수 있지만
a) 파일명을 문자열로 입력해야 합니다.
b) 파일이 런타임에 발견되지 않을 가능성을 받아들여야 합니다.
그게 내가 생각하는 전부야
핵심은 기본적으로 질문에 "프로그래머가 통제할 수 없는 것"이라고 기술되어 있는 것입니다.처음에는 API 프로그래머의 통제를 벗어난 것을 의미한다는 생각이 들었습니다.그러나 실제로 적절하게 사용했을 때 체크된 예외는 클라이언트 프로그래머와 API 프로그래머의 통제 범위를 벗어난 사항이어야 합니다.이것이 체크된 예외를 악용하지 않는 비결이라고 생각합니다.
파일을 열어보니 요점이 잘 드러나는 것 같아요.API 프로그래머는 API가 호출되었을 때 존재하지 않는 파일 이름을 지정할 수 있으며, 사용자가 원하는 것을 반환할 수는 없지만 예외를 두어야 한다는 것을 알고 있습니다.또, 이러한 일이 정기적으로 행해지기 때문에, 클라이언트·프로그래머는, 콜의 작성시에 파일명이 올바르다고 생각할 가능성이 있습니다만, 실행시에, 그 파일명이 잘못되어 버리는 경우도 있습니다.
API는 다음과 같이 명시합니다.당신이 나에게 전화했을 때 이 파일이 존재하지 않는 경우가 있을 것이고, 당신은 그것을 처리하는 것이 좋을 것입니다.
이것은 카운터 케이스로 더 명확해질 것이다.제가 테이블 API를 쓰고 있다고 상상해 보세요.다음 메서드를 포함한 API를 갖춘 테이블 모델을 어딘가에 가지고 있습니다.
public RowData getRowData(int row)
API 프로그래머로서 일부 클라이언트가 테이블 외부에 있는 행이나 행 값을 음수 값으로 전달할 수 있다는 것을 알고 있습니다.그래서 체크 마크를 붙인 예외를 던져 클라이언트에 강제적으로 처리하도록 유도할 수도 있습니다.
public RowData getRowData(int row) throws CheckedInvalidRowNumberException
(물론 '체크'라고는 할 수 없습니다.)
이는 선택된 예외의 잘못된 사용법입니다.클라이언트 코드는 행 데이터를 가져오기 위한 호출로 가득 차게 되며, 각 콜은 시도/캐치를 사용해야 하며, 그 이유는 무엇입니까?잘못된 행이 검색되었음을 사용자에게 보고합니까?그렇지 않을 수도 있습니다. 왜냐하면 테이블 보기를 둘러싼 UI가 무엇이든 간에 사용자가 잘못된 행을 요청하는 상태가 되어서는 안 되기 때문입니다.클라이언트 프로그래머의 버그입니다.
API 프로그래머는 여전히 클라이언트가 이러한 버그를 코드화할 것이라고 예상할 수 있으며 런타임 예외와 함께 처리해야 합니다.IllegalArgumentException
.
에 체크 마크가 붙어 있는 경우getRowData
이것은 분명히 Hejlsberg의 게으른 프로그래머가 빈 캐치만 추가하는 결과를 초래할 것이다.이 경우, 잘못된 행 값은 테스터나 클라이언트 개발자의 디버깅에도 분명하지 않고, 오히려 원인을 특정하기 어려운 연쇄 에러로 이어집니다.아리안 로켓은 발사 후 폭발할 것이다.
자, 여기 문제가 있습니다.체크된 예외가FileNotFoundException
클라이언트 프로그래머에게 가장 유용한 방법으로 API를 정의하기 위한 API 프로그래머 툴박스의 필수 도구입니다.근데...CheckedInvalidRowNumberException
큰 불편이 되어, 잘못된 프로그래밍으로 이어지기 때문에 피해야 합니다.하지만 어떻게 차이를 구별하는지.
나는 그것이 정확한 과학이 아니라고 추측하고 그것이 Hejlsberg의 주장을 어느 정도 뒷받침하고 정당화한다고 추측한다.하지만 저는 아기를 욕조에 버리는 것을 좋아하지 않습니다.그러므로 여기에서 체크된 좋은 예외와 나쁜 예외를 구별하기 위한 몇 가지 규칙을 추출해 보겠습니다.
클라이언트가 제어할 수 없는 상태 또는 닫힌 상태 vs 열린 상태:
선택된 예외는 에러 케이스가 API와 클라이언트프로그래머 양쪽에서 제어할 수 없는 경우에만 사용해야 합니다.이는 시스템의 개방성 또는 폐쇄성과 관련이 있습니다.클라이언트 프로그래머가 테이블 뷰(폐쇄 시스템)에서 행을 추가 및 삭제하는 모든 버튼, 키보드 명령 등을 제어할 수 있는 제약된 UI에서 존재하지 않는 행에서 데이터를 가져오려고 하면 클라이언트 프로그래밍 버그가 됩니다.파일 베이스의 operating system에서는, 임의의 수의 유저/어플리케이션이 파일(오픈 시스템)을 추가 및 삭제할 수 있기 때문에, 클라이언트가 요구하는 파일이 자신도 모르는 사이에 삭제되었을 가능성이 있기 때문에, 그 처리를 기대할 수 있습니다.
유비쿼티:
선택한 예외는 클라이언트가 자주 하는 API 호출에서는 사용하지 마십시오.클라이언트 코드의 많은 장소로부터가 빈번하게 발생하고 있습니다만, 시간적으로는 빈번하지 않습니다.클라이언트 코드가 같은 파일을 많이 열려고 하지 않는 경향이 있는데 테이블 뷰가
RowData
여러 가지 방법으로 사방을 돌아다니고 있어요.특히, 나는 많은 코드를 쓸 것이다.if (model.getRowData().getCell(0).isEmpty())
매번 시도와 시도만으로 싸매야 하는 것은 고통스러울 것이다.
사용자에게 통지:
선택된 예외는 최종 사용자에게 유용한 오류 메시지가 표시될 수 있는 경우에 사용해야 합니다.이것은 제가 위에서 제기했던 "그럴 때 어떻게 할 것인가?"라는 질문입니다.항목 1과도 관련이 있다.클라이언트-API 시스템 외부의 원인으로 파일이 존재하지 않을 수 있으므로 사용자에게 합리적으로 알릴 수 있습니다.
"Error: could not find the file 'goodluckfindingthisfile'"
당신의 잘못된 행 번호는 내부 버그에 의한 것이며 사용자의 과실이 없기 때문에 당신이 그들에게 줄 수 있는 유용한 정보는 정말 없습니다.만약 당신의 앱이 런타임 예외를 콘솔로 넘어가지 않는다면, 그것은 아마도 다음과 같은 추악한 메시지를 그들에게 줄 것이다.
"Internal error occured: IllegalArgumentException in ...."
즉, 클라이언트프로그래머가 사용자에게 도움이 되는 방법으로 예외를 설명할 수 없다고 생각되면 체크 마크를 붙인 예외를 사용하지 않는 것이 좋습니다.
그게 내 규칙이야다소 교묘하고 예외도 있을 것입니다(정리해 주실 수 있으면 도와 주세요).하지만 제 주된 주장은 다음과 같은 경우가 있다는 겁니다FileNotFoundException
여기서 체크된 예외는 API 계약의 일부로서 파라미터 유형만큼 중요하고 유용합니다.그래서 우리는 단지 그것이 오용되었다고 해서 그것을 없애서는 안 된다.
미안해, 이렇게 길고 와플하게 하려던 건 아니었어두 가지 제안으로 마치겠습니다.
A: API 프로그래머: 체크된 예외는 유용성을 유지하기 위해 신중하게 사용합니다.의심스러운 경우 체크되지 않은 예외를 사용합니다.
B: 클라이언트 프로그래머: 개발 초기에 랩된 예외(구글 검색)를 작성하는 습관을 들입니다.JDK 1.4 이후는 다음 중 하나의 컨스트럭터를 제공합니다.RuntimeException
직접 만들 수도 있어요.다음은 컨스트럭터입니다.
public RuntimeException(Throwable cause)
그리고 체크된 예외를 처리해야 할 때 게으름을 느낄 때(혹은 API 프로그래머가 체크된 예외를 사용하는 데 너무 열심이었다고 생각될 때) 예외를 삼키고 랩하고 다시 던지는 습관을 들이세요.
try {
overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
throw new RuntimeException(exception);
}
이것을 IDE의 작은 코드 템플릿 중 하나에 넣고 귀찮을 때 사용하세요.이렇게 하면 선택한 예외를 처리해야 하는 경우 런타임에 문제가 발생한 후 다시 돌아와 처리해야 합니다.왜냐하면, 날 믿어줘, 안데르스 헤일스버그는 다시는 그 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이 실행 시간이 아닌 이유예외?다른 모든 언어로 IO 예외를 허용할 수 있으며, 이 예외를 처리하기 위해 아무것도 하지 않으면 응용 프로그램이 중지되고 보기 쉬운 스택 트레이스를 얻을 수 있습니다.이게 일어날 수 있는 최선의 일이에요.
전체 스트림 쓰기 프로세스에서 모든 IOException을 캡처하고 프로세스를 중단한 후 오류 보고 코드로 이동하는 예에서 두 가지 방법을 사용할 수 있습니다. Java에서는 모든 콜레벨, 심지어 자체에서 IO가 수행되지 않는 레벨에 'throw IOException'을 추가하지 않고서는 이 작업을 수행할 수 없습니다.이러한 메서드는 예외처리에 대해 알 필요가 없습니다.서명에 예외를 추가해야 합니다.
- 불필요하게 커플링을 증가시킨다.
- 인터페이스 시그니처를 변경하기 매우 쉬워집니다.
- 코드를 읽기 어렵게 만듭니다.
- 프로그래머의 일반적인 반응은 'throws Exception', 'catch (Exception e) {}', 또는 모든 것을 런타임으로 래핑함으로써 시스템을 물리치는 것입니다.예외(이 때문에 디버깅이 어려워집니다.
게다가 다음과 같은 터무니없는 라이브러리 예외도 많이 있습니다.
try {
httpconn.setRequestMethod("POST");
} catch (ProtocolException e) {
throw new CanNeverHappenException("oh dear!");
}
이렇게 우스꽝스러운 쓰레기로 코드를 엉망으로 만들어야 할 때, 체크된 예외들이 많은 비난을 받는 것은 당연하다. 비록 이것은 단순한 형편없는 API 설계일 뿐이지만 말이다.
또 다른 특별한 악영향은 컨트롤의 반전(Inversion of Control)으로 컴포넌트A가 범용 컴포넌트B에 콜백을 공급합니다.컴포넌트 A는 예외를 자신의 콜백에서 컴포넌트B를 호출한 장소로 되돌리도록 할 수 있지만, 그렇게 하면 B에 의해 고정되는 콜백인터페이스가 변경되기 때문에 할 수 없습니다.A은(는) 실제 예외를 런타임으로 래핑하는 것만으로 실행할 수 있습니다.예외, 쓰기에는 아직 예외 처리 보일러 플레이트입니다.
Java 및 표준 라이브러리의 의미인 보일러 플레이트, 보일러 플레이트, 보일러 플레이트에 구현된 예외를 확인했습니다.이미 장황한 언어로 말하면 이것은 승리가 아니다.
체크된 예외에 대해 모든 이유를 재탕하기보다는 하나만 고르겠습니다.이 코드 블록을 작성한 횟수를 잊어버렸습니다.
try {
// do stuff
} catch (AnnoyingcheckedException e) {
throw new RuntimeException(e);
}
99%는 어쩔 수 없어요.마지막으로 블록은 필요한 청소를 수행합니다(또는 최소한 해야 합니다).
이런 걸 본 횟수도 잊었어요
try {
// do stuff
} catch (AnnoyingCheckedException e) {
// do nothing
}
왜? 누군가는 그걸 감당해야 하고 게을러서.틀렸나요?물론이죠. 그런 일이 있나요?그렇고 말고요.이것이 체크되지 않은 예외라면 어떻게 될까요?앱이 방금 꺼졌을 것입니다(예외를 삼키는 것보다 더 좋습니다).
또한 java.text와 같이 예외를 흐름 제어의 한 형태로 사용하는 짜증나는 코드도 있습니다.형식은 그렇다.Bzzt. 땡.사용자가 폼의 숫자 필드에 "abc"를 입력하는 것도 예외는 아닙니다.
좋아요, 세 가지 이유가 있었나 봐요.
이게 오래된 질문인건 알지만 나는 체크된 예외사항들과 씨름하며 시간을 보냈고 덧붙일 것이 있다.이 정도로만 봐주세요!
나의 주된 불만은 예외적으로 다형성을 망친다는 것이다.폴리모픽 인터페이스에서는 플레이를 좋게 할 수 없습니다.
좋은 Java 인터페이스를 사용하세요.다음과 같은 일반적인 메모리 내 구현이 있습니다.ArrayList
그리고.LinkedList
또한 골격 클래스도 있어 새로운 유형의 목록을 쉽게 설계할 수 있습니다.읽기 전용 리스트의 경우는, 다음의 2개의 방법만을 실장할 필요가 있습니다.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
interface, 아이템을 취득할 수 있습니다(list.get(123)
또는 목록을 반복합니다.for (Widget w : list) ...
)에 대해 알 필요가 없습니다.WidgetList
그 자체입니다.이 목록을 범용 목록을 사용하는 표준 메서드에 전달할 수도 있고,Collections.synchronizedList
. 이를 사용하는 코드는 "위젯"이 즉석에서 구성되었는지, 배열에서 생성되었는지, 파일 또는 데이터베이스로부터 읽혔는지, 네트워크를 통해 읽혔는지, 미래의 하위 공간 릴레이에서 읽혔는지 알 필요도 없습니다.이 기능은 정상적으로 동작합니다.List
인터페이스가 올바르게 실장되어 있습니다.
하지만 그렇지 않다.위의 클래스는 컴파일되지 않습니다.파일 액세스 메서드에 의해IOException
체크 마크를 붙인 예외입니다.이 예외는 "선택 또는 지정"해야 합니다.rown으로 지정할 수 없습니다.컴파일러는 이를 허용하지 않습니다.이는 계약 위반이기 때문입니다.List
인터페이스입니다.그리고 할 수 있는 유용한 방법은 없다.WidgetList
(나중에 자세히 설명하겠지만) 그 자체로 예외를 처리할 수 있습니다.
체크된 예외를 검출하여 체크되지 않은 예외로 다시 던지기만 하면 되는 것 같습니다.
@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은 정확하게 이 케이스에 대한 클래스를 추가했습니다: 캐치 및 재투입용.IOException
s는 다형성 방법 경계에 걸쳐 있습니다.내 말이 맞아!)
따라서 체크된 예외는 이러한 경우에는 작동하지 않습니다.던지면 안 돼요.똑똑하긴 마찬가지야Map
데이터베이스 또는 구현에 의해 백업됩니다.java.util.Random
COM 포트를 통해 양자 엔트로피 소스에 연결됩니다.폴리모픽 인터페이스의 실장에 의해 새로운 조작을 시도하면 체크된 예외의 개념은 실패합니다.그러나 체크된 예외는 매우 교묘하기 때문에 여전히 안심할 수 없습니다.왜냐하면 여전히 하위 수준의 메서드를 포착하여 코드를 혼란스럽게 하고 스택 트레이스를 혼란스럽게 하기 때문입니다.
유비쿼터스 인터페이스가 체크된 예외를 발생시키는 것을 호출하면 종종 이 코너로 되돌아간다는 것을 알 수 있습니다.예외는 그대로 둘 수 없기 때문에 코드를 캐치하고 재투입하는 것 밖에 할 수 없습니다.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
스택 트레이스를 깔끔하게!"Widget List" 예제를 다시 사용합니다.
@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
.
다행스럽게도throw t;
여기서 진술은 합법이다, 비록 타입이t
검출된 예외의 재투기에 관한 규칙이 Java 7에 추가되어 체크됩니다.
체크된 예외가 다형성을 충족하면 그 반대도 문제가 됩니다.즉, 메서드가 체크된 예외를 잠재적으로 슬로우하는 것으로 지정되었지만 오버라이드된 구현이 그렇지 않은 경우입니다.예를 들어 추상 클래스의write
모두 지정되는 메서드throws IOException
.는 실제 I/O 소스가 아닌 메모리 내 어레이에 쓰는 서브 클래스입니다.덮어쓰기write
메서드는 원인이 될 수 없습니다.IOException
s, s, s, s, s, s, s, s, s, s.throws
이 조항은 캐치 오어 피싱 요건에 대해 걱정하지 않고 전화를 걸 수 있습니다.
항상 그렇지는 않다.라고 가정해 보자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
에 의해 던져진write
에 대한 호출OutputStream
단, 변수의 선언 유형을ByteArrayOutputStream
컴파일러는, 「투입할 수 없다」라고 하는 체크 마크를 붙이려고 하고 있는 것에 대해 질책합니다.
그 규칙은 황당한 문제를 일으킨다.예를 들어, 세 가지 중 하나는write
방법OutputStream
에 의해 덮어쓰지 않는다.ByteArrayOutputStream
구체적으로는write(byte[] data)
를 호출하여 전체 배열을 쓰는 편리한 방법입니다.write(byte[] data, int offset, int length)
오프셋 0과 배열 길이를 지정합니다. ByteArrayOutputStream
는 3개의 패킷 방식을 덮어쓰지만 1개의 패킷 편의 방식을 그대로 상속합니다.상속된 방법은 정확히 올바른 작업을 수행하지만 원하지 않는 방법이 포함되어 있습니다.throws
절을 클릭합니다.그것은 아마도 설계상 실수였을 것이다.ByteArrayOutputStream
단, 예외를 포착한 코드와의 소스 호환성이 깨지기 때문에 수정할 수 없습니다.예외는 지금까지, 지금까지, 지금까지, 앞으로도, 앞으로도 발생하지 않습니다.
이 규칙은 편집 및 디버깅 시에도 귀찮습니다.예를 들어 메서드 호출을 일시적으로 코멘트하여 체크된 예외가 발생했을 경우 컴파일러는 로컬의 존재에 대해 불만을 제기합니다.try
그리고.catch
블록.그래서 저도 코멘트를 해야 합니다.그리고 이제 그 안에서 코드를 편집할 때 IDE가 잘못된 수준으로 들여쓰게 됩니다.왜냐하면{
그리고.}
코멘트 아웃 되어 있습니다.아! 사소한 불만이지만 예외적으로 확인되는 건 문제를 일으키는 것뿐인 것 같아요.
거의 다 했어요.체크된 예외에 대한 나의 마지막 불만 사항은 대부분의 콜 사이트에서 당신이 그것들을 가지고 할 수 있는 유용한 방법이 없다는 것이다.이상적으로는 문제가 발생했을 때 사용자에게 문제를 통지하거나 필요에 따라 작업을 종료하거나 재시도할 수 있는 적절한 애플리케이션 고유의 핸들러가 필요합니다.이것은 스택 상위에 있는 핸들러만이 실행할 수 있습니다.이는 전체적인 목표를 알고 있는 핸들러이기 때문입니다.
대신 컴파일러를 종료하기 위한 방법으로 널리 사용되는 다음과 같은 관용어가 있습니다.
try {
...
} catch (SomeStupidExceptionOmgWhoCares e) {
e.printStackTrace();
}
GUI 또는 자동 프로그램에서는 인쇄된 메시지가 표시되지 않습니다.게다가 예외 후에 나머지 코드를 계속 사용할 수 있습니다.예외는 실제로 오류가 아닐까요?그럼 인쇄하지 마세요.그렇지 않으면 다른 무언가가 잠시 후에 폭발하여 원래 예외 개체가 사라집니다.이 관용구는 베이직과 다를 바 없다On Error Resume Next
또는 PHP의error_reporting(0);
.
어떤 종류의 로거 클래스를 호출하는 것은 그다지 좋지 않습니다.
try {
...
} catch (SomethingWeird e) {
logger.log(e);
}
그것은 나태하다e.printStackTrace();
여전히 불확실한 상태의 코드를 가지고 있습니다.또한 특정 로깅 시스템 또는 기타 핸들러의 선택은 응용 프로그램에 따라 다르므로 코드 재사용에 악영향을 미칩니다.
하지만 기다려!응용 프로그램 고유의 핸들러를 쉽고 보편적인 방법으로 찾을 수 있습니다.콜 스택의 상위(또는 스레드의 수집되지 않은 예외 처리기로 설정됨)입니다.따라서 대부분의 경우 예외만 스택 위로 던지면 됩니다.예.,throw e;
체크한 예외가 방해가 됩니다.
체크된 예외는 언어가 설계되었을 때 좋은 생각처럼 들리겠지만, 실제로는 모두 귀찮고 유익하지 않다는 것을 알게 되었습니다.
스택 트레이스를 표시하거나 소리 없이 크래시하는 것은 아닙니다.레이어간의 에러를 전달할 수 있는 것에 관한 것입니다.
체크된 예외의 문제는 중요한 세부 사항(예외 클래스)을 받아들이도록 장려한다는 것입니다.이 세부사항을 무시하지 않으려면 앱 전체에 스로우 선언을 계속 추가해야 합니다.즉, 1) 새로운 예외 유형이 많은 함수 시그니처에 영향을 주고 2) 실제로 포착하고 싶은 예외의 특정 인스턴스를 놓칠 수 있습니다(예를 들어 파일에 데이터를 쓰는 함수의 secondary 파일을 엽니다).secondary 파일은 옵션입니다.따라서 에러는 무시할 수 있지만 시그니처는throws IOException
간과하기 쉽습니다).
저는 지금 어플리케이션으로 이 상황을 다루고 있습니다.거의 예외는 AppSpecificException으로 재패키지했습니다.이렇게 하면 시그니처가 정말 깨끗해져서 폭발할 염려가 없어졌습니다.throws
서명에 포함.
물론, 현재는, 보다 높은 레벨의 에러 처리를 전문화해, 재시도 로직등을 실장할 필요가 있습니다.단, 모든 것이 AppSpecificException이므로 "IOException이 느려지면 재시도" 또는 "ClassNotFound가 던져지면 완전히 중단"이라고 말할 수 없습니다.코드와 서드파티 코드 사이를 통과할 때 몇 번이고 재패키지되기 때문에 진정한 예외에 도달할 수 있는 확실한 방법은 없습니다.
이것이 바로 제가 파이썬의 예외 처리의 열렬한 팬인 이유입니다.원하는 것만 잡을 수 있고 다룰 수 있습니다.다른 모든 것은 당신이 직접 손질한 것처럼 거품이 일어요.
저는 몇 번이고 반복해서, 그리고 제가 언급한 프로젝트 내내 예외 처리는 세 가지 범주로 분류된다는 것을 알게 되었습니다.
- 특정 예외를 포착하여 처리합니다.예를 들어 재시도 로직을 구현하기 위해서입니다.
- 다른 예외를 포착하여 다시 던집니다.여기서 발생하는 모든 것은 보통 로깅이며 보통 "Unable to open $filename"과 같은 진부한 메시지입니다.이러한 오류는 사용자가 어떻게 할 수 없는 것이며, 이를 처리할 수 있는 것은 상급자뿐입니다.
- 모든 것을 잡아 오류 메시지를 표시합니다.이것은 통상 디스패처의 루트이며, 예외 이외의 메커니즘(팝업 다이얼로그, RPC 에러 오브젝트 마샬링 등)을 통해 발신자에게 에러를 전달할 수 있는 것을 확인합니다.
SNR
첫째, 선택된 예외는 코드의 "신호 대 잡음 비"를 감소시킵니다.Anders Hejlsberg는 또한 유사한 개념인 명령형 vs 선언형 프로그래밍에 대해서도 이야기합니다.어쨌든, 다음의 코드 스니펫을 검토해 주세요.
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, 체크된 예외에 대한 주장을 날카롭게 다루고 있습니다.짧은 맛보기:
적어도 Java에서 구현되는 방식인 throws 절에서는 예외를 처리하도록 강제할 필요는 없지만 처리하지 않으면 통과할 수 있는 예외를 정확하게 인식하도록 강제할 수 있습니다.선언된 예외를 캐치하거나 자신의 throws 절에 넣어야 합니다.이 요건을 회피하기 위해 사람들은 우스꽝스러운 일을 한다.예를 들어, 모든 방법을 "throws Exception"으로 장식합니다.그러면 기능이 완전히 상실되고 프로그래머가 더 골칫덩어리를 쓰게 됩니다.그건 아무에게도 도움이 안 돼요.
저는 항상 체크된 예외를 선호해 왔고, 왜 에서 체크되지 않는 것을 싫어하는지 생각하기 시작했습니다.네, 하지만 난 체크된 예외처럼 행동하지 않는다는 걸 깨달았어요
질문에 답하자면, 스택 트레이스를 표시하는 프로그램이 좋습니다.가능하면 매우 추한 트레이스를 표시하는 것이 좋습니다.어플리케이션을 최악의 오류 메시지 더미로 만들고 싶다.
그 이유는, 만약 그렇게 된다면, 제가 고쳐야 하고, 바로 고쳐야 하기 때문입니다.저는 문제가 생겼다는 것을 즉시 알고 싶습니다.
실제로 몇 번이나 예외를 처리합니까?예외를 잡는 것이 아니라 예외에 대처하는 것을 말하는 건가요?다음 내용을 쓰는 것은 너무 쉽습니다.
try {
thirdPartyMethod();
} catch(TPException e) {
// this should never happen
}
그리고 이것이 나쁜 관행이라고 말할 수 있고, '정답'은 예외적인 것을 실행하는 것이라고 말할 수 있다는 것을 알지만(맞혀보세요, 로그 기록하시겠습니까?) 실제 세계에서는 대부분의 프로그래머가 그렇게 하지 않습니다.
네, 만약 제가 예외사항을 잡을 필요가 없다면, 저는 그렇게 하고 싶지 않습니다. 그리고 제가 망쳤을 때 제 프로그램이 크게 폭발하기를 원합니다.묵묵히 실패하는 것은 최악의 결과이다.
"Effective Java Exceptions(유효 Java 예외)" 문서에서는 선택되지 않은 경우 및 선택한 예외 사용 시기에 대해 자세히 설명합니다.다음은 해당 기사에서 주요 사항을 강조하기 위해 인용한 몇 가지입니다.
만일의 경우:방법의 의도된 목적에 따라 표현될 수 있는 방법의 대안적 대응을 요구하는 예상 조건.메서드의 호출자는 이러한 조건을 예상하고 이에 대처하기 위한 전략을 가지고 있다.
장애: 메서드의 내부 구현을 참조하지 않고는 설명할 수 없는 메서드의 의도된 목적을 달성할 수 없는 계획되지 않은 상태입니다.
(SO는 테이블을 허용하지 않으므로 원래 페이지에서 다음 내용을 읽어보십시오.)
만일의 사태에 대비
- 다음과 같이 간주됩니다.설계의 일부입니다.
- 발생할 것으로 예상됨: 정기적이지만 거의 발생하지 않음
- 누가 신경써요?메서드를 호출하는 업스트림코드
- 예:대체 리턴 모드
- 최적의 매핑:선택된 예외
결함.
- 그것은 다음과 같이 여겨진다: 끔찍한 놀라움
- 예상되는 일:절대.
- 누가 신경써요?문제를 해결할 필요가 있는 사람
- 예: 프로그래밍 오류, 하드웨어 오작동, 구성 오류, 파일 누락, 사용 불가능한 서버
- 최적의 매핑:체크되지 않은 예외
저는 지난 3년 동안 여러 개발자들과 비교적 복잡한 애플리케이션 작업을 해왔습니다.적절한 에러 처리와 함께 체크된 예외를 자주 사용하는 코드 베이스와 그렇지 않은 코드 베이스가 있습니다.
지금까지 Checked Exceptions(체크된 예외)를 사용하여 코드 베이스로 작업하는 것이 더 쉬웠습니다.다른 사람의 API를 사용하면 코드를 호출했을 때 어떤 종류의 에러 상태가 발생할 수 있는지 정확하게 알 수 있고 로깅, 표시, 무시 중 하나를 통해 적절히 처리할 수 있어 좋습니다(예, ClassLoader 구현 등 예외를 무시하는 유효한 경우가 있습니다).그러면 내가 쓰고 있는 코드가 회복될 수 있습니다.모든 런타임 예외는 캐시되고 일반적인 오류 처리 코드로 처리될 때까지 전파됩니다.특정 수준에서 실제로 처리하지 않거나 프로그래밍 로직 오류가 있다고 생각되는 체크된 예외를 발견하면 런타임으로 바꿉니다.예외로 하고 거품을 내도록 해라.절대 정당한 이유 없이 예외를 받아들이지 마십시오(또한 이를 위한 정당한 이유는 거의 없습니다).
예외를 체크하지 않은 코드 베이스로 작업하면 함수를 호출할 때 무엇을 기대할 수 있는지 미리 알기 어렵고, 그 때문에 어떤 것이 심하게 파손될 수 있습니다.
물론 이것은 모두 선호도와 개발자의 스킬의 문제입니다.프로그래밍과 에러 처리의 양쪽 모두가 똑같이 효과적일 수 있기 때문에(또는 효과적이지 않을 수도 있습니다), 일방향이라고는 말할 수 없습니다.
전반적으로, 특히 개발자가 많은 대규모 프로젝트에서는 Checked Exceptions와 함께 작업하기가 더 쉽습니다.
요컨대:
예외는 API 설계 질문입니다. -- 그 이상도 이하도 아닙니다.
선택된 예외에 대한 인수:
선택한 예외가 좋지 않은 이유를 이해하려면 질문을 돌려서 다음과 같이 질문해 보겠습니다.체크된 예외가 매력적인 경우 또는 이유는 무엇입니까? 즉, 컴파일러가 예외 선언을 강제하도록 하는 이유는 무엇입니까?
답은 명확합니다.경우에 따라서는 예외를 검출할 필요가 있습니다.이것은, 콜 되고 있는 코드가, 관심 있는 에러의 특정의 예외 클래스를 제공하는 경우에만 가능합니다.
따라서 체크된 예외에 대한 논거는 컴파일러가 프로그래머에게 어떤 예외가 발생하는지 선언하도록 강요하고 프로그래머가 특정 예외 클래스와 그 원인이 되는 오류를 문서화하는 것입니다.
하지만 실제로는 너무 자주 꾸러미가com.acme
던지기만 하면AcmeException
특정 서브클래스가 아닌그 후, 발신자는 처리, 선언, 또는 재시그널링을 실시할 필요가 있습니다.AcmeExceptions
, 그러나 아직 확실하게 할 수 없습니다.AcmeFileNotFoundError
발생하거나AcmePermissionDeniedError
.
그래서 만약 당신이 단지 이 모든 것에 관심이 있다면AcmeFileNotFoundError
해결책은 기능 요구를 ACME 프로그래머에게 제출하고 그 서브클래스의 실장, 선언 및 문서화를 지시하는 것입니다.AcmeException
.
그럼 왜 신경써?
따라서 체크된 예외가 있더라도 컴파일러는 프로그래머에게 유용한 예외를 강요할 수 없습니다.그것은 API의 품질에 관한 문제일 뿐이다.
그 결과, 체크 마크가 붙어 있지 않은 언어는 일반적으로 그다지 나빠지지 않습니다.프로그래머는 특정되지 않은 일반 인스턴스를 던지고 싶을 수 있습니다.Error
수업이라기보다AcmeException
하지만 API의 품질에 대해 조금이라도 신경을 쓴다면, 그들은 새로운 API를 도입하는 방법을 배울 것이다.AcmeFileNotFoundError
결국.
전반적으로 예외의 명세 및 문서화는 예를 들어 일반적인 방법의 명세 및 문서화와 크게 다르지 않다.이것들도 API 설계 질문입니다.프로그래머가 유용한 기능을 실장하거나 내보내는 것을 잊은 경우 API를 개선하여 유용하게 작업할 수 있도록 해야 합니다.
이 논리를 따르면 Java와 같은 언어에서 흔히 볼 수 있는 예외 선언, 포착 및 재투입의 "급행"은 거의 가치를 창출하지 못하는 것이 분명합니다.
Java VM은 예외를 체크하지 않습니다.Java 컴파일러만이 예외를 체크하고 예외 선언이 변경된 클래스 파일은 런타임에 호환됩니다.Java VM 보안은 체크된 예외에 의해 향상되지 않고 코딩 스타일만 개선됩니다.
예외 카테고리
예외에 대해 이야기할 때 나는 항상 Eric Lippert의 Vexing 예외 블로그 기사를 참조한다.그는 다음과 같은 범주에 예외를 둡니다.
- 치명적 - 이러한 예외는 사용자의 잘못이 아닙니다. 예방할 수 없고 적절하게 처리할 수 없습니다.예를들면,
OutOfMemoryError
또는ThreadAbortException
. - Bonehead(본헤드) - 이러한 예외는 사용자의 잘못입니다. 이를 방지해야 하며 코드 내의 버그를 나타냅니다.예를들면,
ArrayIndexOutOfBoundsException
,NullPointerException
또는 임의의IllegalArgumentException
. - 성가신 일 - 이러한 예외는 예외적인 것이 아닙니다.당신의 잘못도 아닙니다.그것을 막을 수는 없지만, 당신은 그것들을 처리해야 합니다.그것들은 종종 던지기 등 부적절한 설계 결정의 결과입니다.
NumberFormatException
부터Integer.parseInt
를 제공하는 대신Integer.tryParseInt
해석 실패 시 부울 false를 반환하는 메서드. - 외인성 - 이러한 예외는 일반적으로 예외이며, 사용자의 잘못이 아닙니다. 예방할 수는 없지만(합리적으로) 대처해야 합니다.예를들면,
FileNotFoundException
.
API 사용자:
- 치명적 또는 골머리 예외를 처리해서는 안 됩니다.
- 는 성가신 예외를 처리해야 하지만 이상적인 API에서는 발생하지 않아야 합니다.
- 는 외인성 예외를 처리해야 합니다.
체크된 예외
API 사용자가 특정 예외를 처리해야 한다는 사실은 발신자와 착신자 간의 메서드 계약의 일부입니다.계약에서는, 특히, 착신측이 예상하는 인수의 수와 타입, 발신자가 예상할 수 있는 반환치의 타입, 및 발신자가 처리할 것으로 예상되는 예외등이 지정됩니다.
API에는 성가신 예외가 존재해서는 안 되기 때문에 메서드의 계약에 포함시키려면 이러한 외부 예외만 체크해야 합니다.예외는 비교적 적기 때문에 API에 체크된 예외는 비교적 적어야 합니다.
선택된 예외는 처리해야 하는 예외입니다.예외를 처리하는 방법은 삼키는 것만큼이나 간단할 수 있습니다.자! 예외는 처리됩니다.마침표개발자가 그런 식으로 처리하길 원한다면 좋아요.하지만 그는 예외를 무시할 수 없었고, 경고를 받았다.
API 문제
단, 번거롭고 치명적인 예외(JCL 등)를 체크한 API는 API 사용자에게 불필요한 부담을 줍니다.이러한 예외는 처리해야 하지만 예외가 너무 일반적이기 때문에 처음부터 예외가 아니었거나 처리할 때 아무것도 할 수 없습니다.이로 인해 Java 개발자는 체크된 예외를 싫어하게 됩니다.
또한 많은 API에는 적절한 예외 클래스 계층이 없기 때문에 모든 종류의 비균형 예외 원인이 하나의 체크된 예외 클래스로 나타납니다(예:IOException
이로 인해 Java 개발자는 체크된 예외를 싫어하게 됩니다.
결론
외생적인 예외는 사용자의 잘못이 아니며 예방할 수 없으며 처리되어야 하는 예외입니다.이들은 발생할 수 있는 모든 예외의 작은 서브셋을 형성합니다.API는 외부 예외만 체크하고 다른 모든 예외는 체크해야 합니다.이를 통해 API가 개선되고 API 사용자에게 부담이 줄어들기 때문에 모든 것을 포착하거나 체크되지 않은 예외를 삼키거나 다시 던질 필요가 줄어듭니다.
따라서 자바와 체크된 예외를 미워하지 마십시오.대신 체크된 예외를 과도하게 사용하는 API를 싫어합니다.
네... 체크된 예외는 이상적이지 않고 주의사항이 있지만 목적에 부합합니다.API를 작성할 때 이 API의 계약상 오류가 발생하는 경우가 있습니다.Java와 같이 강력한 정적으로 입력된 언어의 컨텍스트에서 체크된 예외를 사용하지 않을 경우 오류 가능성을 전달하기 위해 임시 문서 및 규약에 의존해야 합니다.이렇게 하면 컴파일러가 처리 오류를 일으킬 수 있는 모든 이점이 제거되고 프로그래머의 호의에 전적으로 의존하게 됩니다.
따라서 C#에서와 같이 체크된 예외를 삭제하면 오류 가능성을 어떻게 프로그래밍 및 구조적으로 전달할 수 있습니까?클라이언트 코드에 이러한 오류가 발생할 수 있으며 대처해야 한다는 것을 어떻게 알립니까?
체크된 예외를 다룰 때 모든 종류의 공포가 오용된다고 들었습니다.확실하지만 체크되지 않은 예외도 마찬가지입니다.API가 여러 층으로 쌓이면 오류를 전달하기 위해 구조화된 수단이 반환되기를 바라는 몇 년을 기다려야 합니다.
API 레이어의 하부에 예외가 던져지고 아무도 이 에러가 발생할 수 있다는 것을 몰랐기 때문에 버블이 발생한 경우를 예로 들어 보겠습니다.이것은 호출 코드가 그것을 던졌을 때 매우 그럴듯한 유형의 에러입니다(예를 들어, VogonsTrashingEarthException과 반대되는 FileNotFoundException...이 경우 취급할 수 있는 것이 없기 때문에 취급 여부를 불문하고 상관없습니다).
많은 사람들은 파일을 로딩하지 못하는 것이 거의 항상 이 과정에서 세상의 종말이며 끔찍하고 고통스러운 죽음을 맞이해야 한다고 주장해 왔다.그러니까..네... 네..API를 구축하면 파일이 로딩됩니다.해당 API의 사용자로서 응답할 수 있는 것은..."당신이 뭔데 내 프로그램이 언제 크래쉬해야 하는지 결정해!" 물론 예외가 삼켜지고 흔적을 남기지 않는 선택이나 Marianna 트렌치보다 깊은 스택트레이스를 가진 EletroFlabbingChunkFluxManifoldChuggingException을 선택할 때 나는 조금도 주저하지 않고 후자를 택할 것입니다.예외에 대처하라? 우리는 예외가 새로운 수준의 추상화로 넘어갈 때마다 다시 주조되고 포장되어 실제로 의미가 있는 곳에 있을 수 없을까?
마지막으로, 내가 보는 대부분의 논쟁은 "나는 예외를 다루고 싶지 않다. 많은 사람들은 예외를 다루고 싶어하지 않는다.체크된 예외는 나에게 그것들을 다루도록 강요한다.그래서 체크된 예외는 싫다.그런 메커니즘을 완전히 제거하고 그것을 지옥의 틈으로 몰아넣는 것은 어리석고, 추리력과 비전이 결여되어 있다.
체크된 예외를 삭제하면 함수의 반환 유형도 삭제되고 항상 "임의 유형" 변수를 반환할 수 있습니다.그러면 이제 삶이 훨씬 더 단순해질 거야, 안 그래?
이것은 체크된 예외의 순수한 개념에 반대하는 주장은 아니지만, Java가 그것들을 위해 사용하는 클래스 계층은 기괴한 쇼이다.우리는 항상 그것들을 단순히 "예외"라고 부릅니다.언어사양에서도 그렇게 부르기 때문입니다만, 타입 시스템에서는 어떻게 예외의 이름을 붙이고 나타낼 수 있을까요?
상상하는 수업으로?아니요, 왜냐하면Exception
는 예외이며, 마찬가지로 예외는 다음과 같습니다.Exception
s. 단, 그렇지 않은 예외는 제외한다. Exception
s. 다른 예외는 실제로는 s이기 때문에 다른 종류의 예외이기 때문에 예외는 발생 시 이외에는 발생해서는 안 되며 때로는 발생해야 하는 경우를 제외하고는 포착해서는 안 됩니다.다른 예외도 정의할 수 있기 때문에 그것뿐만이 아닙니다.Exception
이나Error
예외일 뿐입니다.
다음 중 "체크된" 예외는 무엇입니까? Throwable
는 체크된 예외입니다.단, 이 예외도 마찬가지입니다.Error
s(체크되지 않은 예외)가 있습니다.다음으로Exception
이들도 마찬가지입니다.Throwable
및 는 체크된 예외의 주요 유형입니다.단, 그것에도 예외가1개 있습니다.즉, 이 예외도 s일 경우 체크되지 않은 예외의 다른 종류이기 때문입니다.
무엇이냐RuntimeException
이름에서 알 수 있듯 그들은 예외야, 모두 그렇듯이Exception
모든 예외와 마찬가지로 런타임에 발생합니다.단, 단,RuntimeException
는 다른 런타임에 비해 매우 우수합니다.Exception
왜냐하면 그것들은 당신이 바보 같은 실수를 했을 때를 제외하고는 일어나지 않도록 되어있기 때문입니다.RuntimeException
는 절대 아니다Error
s, 그래서 그것들은 예외적으로 잘못되었지만 실제로는 그렇지 않은 것들을 위한 것입니다.Error
s. 단, 실제로는RuntimeException
위해서Error
s. 하지만 모든 예외는 어쨌든 잘못된 상황을 나타내게 되어 있지 않나요?네, 전부 다요.예외는 예외입니다.문서에서는 '정상적인 발생'이라고 설명하고 있기 때문에 예외는 예외입니다.그 때문에, 이러한 예외는 다음과 같습니다.Error
.
어쨌든, 우리는 모든 예외를 중간에서 나누기 때문에Error
s(이것은 예외적인 실행 예외용이므로 체크되지 않음) 및Exception
s(이러한 예외적인 실행 오류에 대한 것이므로 그렇지 않은 경우를 제외하고 확인) 이제 몇 가지 예외 각각에 대해 두 가지 종류가 필요합니다.그래서, 그리고, 그리고, 그리고, 그리고, 그리고, 그리고, 그리고 가 필요합니다.
예외 체크 마크가 켜져 있어도 컴파일러를 속여 체크 마크를 하지 않고 버리는 방법은 항상 있습니다.이 경우, 다른 경우를 제외하고, 또는 ('심각한 예외'에 한함)또는 (와 무관), 또는 ('심각한 예외'에 한함)로 토할 수 있습니다.
아, 그리고 우리는 자바8의 멋진 신제품을 잊어서는 안 된다.RuntimeException
예외는 I/O 오류(예외를 발생시키지 않음)로 인해 발생하는 예외 체크 개념을 창 밖으로 던질 수 있도록 설계되어 있습니다.이 예외는 매우 다루기 어렵기 때문에 예외 체크가 필요하지 않습니다.
자바 고마워!
이 기사는 제가 읽은 Java의 예외 처리에 관한 최고의 텍스트입니다.
그것은 체크되지 않은 예외보다 체크되지 않은 것을 선호하지만 이 선택은 매우 거칠고 강력한 논거에 근거해 설명된다.
여기 있는 기사 내용을 너무 많이 인용하고 싶지는 않지만(전체적으로 읽는 것이 가장 좋습니다), 이 글에서 체크되지 않은 예외 옹호자의 주장을 대부분 다루고 있습니다.특히 이 주장(대부분 인기 있는 것처럼 보이는 것)은 다음과 같습니다.
API 레이어의 하부에 예외가 던져지고 아무도 이 에러가 발생할 수 있다는 것을 몰랐기 때문에 버블이 발생한 경우를 예로 들어 보겠습니다.이것은 호출 코드가 그것을 던졌을 때 매우 그럴듯한 유형의 에러입니다(예를 들어, VogonsTrashingEarthException과 반대되는 FileNotFoundException...이 경우 취급할 수 있는 것이 없기 때문에 취급 여부를 불문하고 상관없습니다).
작성자는 다음과 같이 응답합니다.
모든 런타임 예외가 검출되어 어플리케이션의 맨 위에 전파되어서는 안 된다고 가정하는 것은 완전히 잘못된 것입니다. (...) 시스템/비즈니스 요건에 따라 명확하게 처리되어야 하는 모든 예외적인 조건에 대해 프로그래머는 ca의 조건이 충족되면 이를 포착할 장소와 수행할 작업을 결정해야 합니다.이 작업은 컴파일러의 경고가 아니라 응용 프로그램의 실제 요구에 따라 수행해야 합니다.그 외의 에러는 모두, 로그가 기록되는 최상위 핸들러에 자유롭게 전파할 수 있도록 할 필요가 있습니다.또한 정상적인(아마도 종료) 액션이 실행됩니다.
그리고 주요 생각 또는 기사는 다음과 같습니다.
소프트웨어의 에러 처리에 관해서는 존재하는 서브루틴 또는 모듈 전체에서 장애가 발생할 가능성이 있다고 가정할 수 있습니다.
따라서 "이 오류가 발생할 수 있다는 사실조차 아무도 몰랐다"는 것은 이 프로젝트에 문제가 있는 것입니다.이러한 예외는 작성자가 제안하는 바와 같이 적어도 가장 일반적인 예외 핸들러(예를 들어 보다 구체적인 핸들러에 의해 처리되지 않는 모든 예외를 처리하는 핸들러)에 의해 처리되어야 합니다.
이 훌륭한 기사를 발견한 사람은 별로 없는 것 같다:-(어느 쪽이 좋을지 망설이는 사람은 시간을 갖고 읽어보라고 진심으로 권한다.
실제로 체크된 예외는 프로그램의 견고성과 정확성을 높입니다(메서드에 의해 발생하는 예외는 기본적으로 특수한 반환 유형입니다).한편, 예외는 「버블 업」되기 때문에, 1개의 방법으로 발생하는 예외를 변경할 때는, 많은 방식(모든 발신자, 발신자의 발신자등)을 변경할 필요가 있는 경우가 많습니다.
Java에서 체크된 예외는 후자의 문제(C# 및 VB)를 해결하지 않습니다.NET은 목욕물과 함께 아기를 버린다.
중간을 취하는 적절한 접근방식은 이 OOPSLA 2005 문서(또는 관련 기술 보고서)에 설명되어 있습니다.
즉, 다음과 같이 말할 수 있습니다.method g(x) throws like f(x)
즉, g는 frows의 모든 예외를 슬로우합니다.Voila, 계단식 변경 문제 없이 예외를 확인했습니다.
학술 논문이지만 체크된 예외 사항의 장단점을 잘 설명해 주기 때문에 읽어 보시기 바랍니다.
문제
예외 처리 메커니즘에서 볼 수 있는 가장 큰 문제는 코드 중복이 대규모로 도입된다는 것입니다.솔직히 말해 봅시다.대부분의 프로젝트에서는 개발자가 예외적으로 해야 할 일은 사용자에게 어떻게든 전달하는 것뿐입니다(또한 경우에 따라서는 스택 트레이스를 이메일로 보내는 등 개발팀에도 전달).따라서 일반적으로 예외가 처리되는 모든 장소에서 동일한 줄/블록의 코드가 사용됩니다.
체크된 예외 유형에 대해 각 catch 블록에서 단순 로깅을 수행한다고 가정합니다.
try{
methodDeclaringCheckedException();
}catch(CheckedException e){
logger.error(e);
}
일반적인 예외일 경우 더 큰 코드베이스에 수백 개의 이러한 트라이캐치 블록이 있을 수 있습니다.이제 콘솔 로깅 대신 팝업 대화 상자 기반 예외 처리를 도입하거나 개발 팀에 이메일을 추가로 보내야 한다고 가정합니다.
잠깐만요...코드에 있는 수백 개의 위치를 전부 편집하는 거야?!무슨 말인지 아시겠죠:-)
해결 방법
이 문제에 대처하기 위해 한 것은 예외 처리를 일원화하기 위해 예외 핸들러(EH)의 개념을 도입하는 것이었습니다.예외를 처리해야 하는 모든 클래스에 대해 예외 핸들러의 인스턴스가 종속성 주입 프레임워크에 의해 주입됩니다.따라서 일반적인 예외 처리 패턴은 다음과 같습니다.
try{
methodDeclaringCheckedException();
}catch(CheckedException e){
exceptionHandler.handleError(e);
}
예외 처리를 커스터마이즈 하려면 , 코드를 1개의 장소(EH 코드)로 변경하기만 하면 됩니다.
물론 더 복잡한 경우에는 몇 가지 EH 서브클래스를 구현하고 DI 프레임워크에서 제공하는 기능을 활용할 수 있습니다.DI 프레임워크 구성을 변경하면 EH 구현을 글로벌하게 쉽게 전환하거나 특별한 예외 처리가 필요한 클래스에 EH의 특정 구현을 제공할 수 있습니다(예: Guice @Named 주석 사용).
That way we can differentiate exception handling behaviour in development and release version of application (eg. development - logging the error and halting the application, prod - logging the error with more details and letting the application continue its execution) with no effort.
Last one thing
Last but not least, it may seem that the same kind of centralisation can be obtained by just passing our exceptions "up" until they arrive to some top level exception handling class. But that leads to cluttering of code and signatures of our methods and introduces maintenance problems mentioned by others in this thread.
One important thing nobody mentioned is how it interferes with interfaces and lambda expressions.
Let's say you define a MyAppException extends Exception
. It is the top level exception inherited by all exceptions thrown by your application. In some places you don't want to react to the particular exceptions, you want the caller to resolve it, so you declare throws MyAppException
.
All looks OK until you want to use someone else's interface. Obviously they don't declare intention to throw MyAppException
, so compiler doesn't allow you to even call your methods that declare throws MyAppException
in there. This is especially painful with java.util.function
.
However, if your exception extends RuntimeException
, there will be no problem with interfaces. You can mention the exception in JavaDoc if you wish. But other than that it just silently bubbless through anything. Of course that means it can terminate your application. But in lots of enterprise software you have exception handling layer and unchecked exceptions save lots of trouble.
To attempt to address just the unanswered question:
If you throw RuntimeException subclasses instead of Exception subclasses then how do you know what you are supposed to catch?
The question contains specious reasoning IMHO. Just because the API tells you what it throws doesn't mean you deal with it in the same way in all cases. To put it another way, the exceptions you need to catch vary depending on the context in which you use the component throwing the exception.
For example:
If I'm writing a connection tester for a database, or something to check the validity of a user entered XPath, then I'd probably want to catch and report on all checked and unchecked exceptions that are thrown by the operation.
If, however, I am writing a processing engine, I will likely treat an XPathException (checked) in the same way as an NPE: I would let it run up to the top of the worker thread, skip the rest of that batch, log the issue (or send it to a support department for diagnosis) and leave feedback for the user to contact support.
As folks have already stated, checked exceptions don't exist in Java bytecode. They are simply a compiler mechanism, not unlike other syntax checks. I see checked exceptions a lot like I see the compiler complaining about a redundant conditional: if(true) { a; } b;
. That's helpful but I might have done this on purpose, so let me ignore your warnings.
The fact of the matter is, you aren't going to be able to force every programmer to "do the right thing" if you enforce checked exceptions and everyone else is now collateral damage who just hates you for the rule you made.
Fix the bad programs out there! Don't try to fix the language to not allow them! For most folks, "doing something about an exception" is really just telling the user about it. I can tell the user about an unchecked exception just as well, so keep your checked exception classes out of my API.
Anders speaks about the pitfalls of checked exceptions and why he left them out of C# in episode 97 of Software Engineering radio.
My writeup on c2.com is still mostly unchanged from its original form: CheckedExceptionsAreIncompatibleWithVisitorPattern
In summary:
Visitor Pattern and its relatives are a class of interfaces where the indirect caller and interface implementation both know about an exception but the interface and direct caller form a library that cannot know.
The fundamental assumption of CheckedExceptions is all declared exceptions can be thrown from any point that calls a method with that declaration. The VisitorPattern reveals this assumption to be faulty.
The final result of checked exceptions in cases like these is a lot of otherwise useless code that essentially removes the compiler's checked exception constraint at runtime.
As for the underlying problem:
My general idea is the top-level handler needs to interpret the exception and display an appropriate error message. I almost always see either IO exceptions, communication exceptions (for some reason APIs distinguish), or task-fatal errors (program bugs or severe problem on backing server), so this should not be too hard if we allow a stack trace for a severe server problem.
Checked exceptions were, in their original form, an attempt to handle contingencies rather than failures. The laudable goal was to highlight specific predictable points (unable to connect, file not found, etc) & ensure developers handled these.
What was never included in the original concept, was to force a vast range of systemic & unrecoverable failures to be declared. These failures were never correct to be declared as checked exceptions.
Failures are generally possible in code, and EJB, web & Swing/AWT containers already cater for this by providing an outermost “failed request” exception-handler. The most basic correct strategy is to rollback the transaction & return an error.
One crucial point, is that runtime & checked exceptions are functionally equivalent. There is no handling or recovery which checked exceptions can do, that runtime exceptions can’t.
The biggest argument against “checked” exceptions is that most exceptions can’t be fixed. The simple fact is, we don’t own the code/ subsystem that broke. We can’t see the implementation, we’re not responsible for it, and can’t fix it.
If our application is not a DB.. we shouldn't try and fix the DB. That would violate the principle of encapsulation.
Particularly problematic have been the areas of JDBC (SQLException) and RMI for EJB (RemoteException). Rather than identifying fixable contingencies as per the original “checked exception” concept, these forced pervasive systemic reliability issues, not actually fixable, to be widely declared.
The other severe flaw in the Java design, was that exception-handling should correctly placed at the highest possible "business" or "request" level. The principle here is "throw early, catch late". Checked exceptions do little but get in the way of this.
We have an obvious issue in Java of requiring thousands of do-nothing try-catch blocks, with a significant proportion (40%+) being miscoded. Almost none of these implement any genuine handling or reliability, but impose major coding overhead.
Lastly, "checked exceptions" are pretty much incompatible with FP functional programming.
Their insistence on "handle immediately" is at odds with both "catch late" exception-handling best practice, and any FP structure which abstracts loops/ or flow of control.
Many people talk about "handling" checked exceptions, but are talking through their hats. Continuing after a failure with null, incomplete or incorrect data to pretend success is not handling anything. It's engineering/ reliability malpractice of the lowest form.
Failing cleanly, is the most basic correct strategy for handling an exception. Rolling back the transaction, logging the error & reporting a "failure" response to the user are sound practice -- and most importantly, prevent incorrect business data being committed to the database.
Other strategies for exception-handling are "retry", "reconnect" or "skip", at the business, subsystem, or request level. All of these are general reliability strategies, and work well/ better with runtime exceptions.
Lastly, it is far preferable to fail, than to run with incorrect data. Continuing will either cause secondary errors, distant from the original cause & harder to debug; or will eventually result in erroneous data being committed. People get fired for that.
See:
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/
I think that this is an excellent question and not at all argumentative. I think that 3rd party libraries should (in general) throw unchecked exceptions. This means that you can isolate your dependencies on the library (i.e. you don't have to either re-throw their exceptions or throw Exception
- usually bad practice). Spring's DAO layer is an excellent example of this.
On the other hand, exceptions from the core Java API should in general be checked if they could ever be handled. Take FileNotFoundException
or (my favourite) InterruptedException
. These conditions should almost always be handled specifically (i.e. your reaction to an InterruptedException
is not the same as your reaction to an IllegalArgumentException
). The fact that your exceptions are checked forces developers to think about whether a condition is handle-able or not. (That said, I've rarely seen InterruptedException
handled properly!)
One more thing - a RuntimeException
is not always "where a developer got something wrong". An illegal argument exception is thrown when you try and create an enum
using valueOf
and there's no enum
of that name. This is not necessarily a mistake by the developer!
A problem with checked exceptions is that exceptions are often attached to methods of an interface if even one implementation of that interface uses it.
Another problem with checked exceptions is that they tend to be misused. The perfect example of this is in java.sql.Connection
's close()
method. It can throw a SQLException
, even though you've already explicitly stated that you're done with the Connection. What information could close() possibly convey that you'd care about?
Usually, when I close() a connection*
, it looks something like this:
try {
conn.close();
} catch (SQLException ex) {
// Do nothing
}
Also, don't get me started on the various parse methods and NumberFormatException... .NET's TryParse, which doesn't throw exceptions, is so much easier to use it's painful to have to go back to Java (we use both Java and C# where I work).
*
As an additional comment, a PooledConnection's Connection.close() doesn't even close a connection, but you still have to catch the SQLException due to it being a checked exception.
The programmer needs to know all of the exceptions that a method may throw, in order to use it correctly. So, beating him over the head with just some of the exceptions does not necessarily help a careless programmer avoid errors.
The slim benefit is outweighed by the burdensome cost (especially in larger, less flexible code bases where constantly modifying the interface signatures is not practical).
Static analysis can be nice, but truly reliable static analysis often inflexibly demands strict work from the programmer. There is a cost-benefit calculation, and the bar needs to be set high for a check that leads to a compile time error. It would be more helpful if the IDE took on the role of communicating which exceptions a method may throw (including which are unavoidable). Although perhaps it would not be as reliable without forced exception declarations, most exceptions would still be declared in documentation, and the reliability of an IDE warning is not so crucial.
The good proves that Checked Exception are not needed are:
- A lot of framework that does some work for Java. Like Spring that wraps JDBC exception to unchecked exceptions, throwing messages to the log
- Lot of languages that came after java, even on top on java platform - they do not use them
- Checked exceptions, it is kind prediction about how the client would use the code that throws an exception. But a developer who writes this code would never know about the system and business that client of code is working in. As an example Interfcace methods that force to throw checked exception. There are 100 implementation over the system, 50 or even 90 of implementations do not throw this exception, but the client still must to catch this exception if he user reference to that interface. Those 50 or 90 implementations tend to handle those exceptions inside themself, putting exception to the log (and this is good behavior for them). What we should do with that? I would better have some background logic that would do all that job - sending message to the log. And If I, as a client of code, would feel I need handle the exception - I will do it. I may forget about it, right - but if I use TDD, all my steps are covered and I know what I want.
- Another example when I'm working with I/O in java, it forces me to check all exception, if file does not exists? what I should do with that? If it does not exists, the system would not go to the next step. The client of this method, would not get expected content from that file - he can handle Runtime Exception, otherwise I should first check Checked Exception, put a message to log, then throw exception up out form the method. No...no - I would better do it automatically with RuntimeEception, that does it / lits up automatically. There is no any sense to handle it manually - I would be happy I saw an error message in the log (AOP can help with that.. something that fixes java). If, eventually, I deice that system should shows pop-up message to the end user - I will show it, not a problem.
I was happy if java would provide me with a choice what to use, when working with core libs, like I/O. Like provides two copies of same classes - one wrapped with RuntimeEception. Then we can compare what people would use. For now, though, many people would better go for some framework on top on java, or different language. Like Scala, JRuby whatever. Many just believe that SUN was right.
We've seen some references to C#'s chief architect.
Here's an alternate point of view from a Java guy about when to use checked exceptions. He acknowledges many of the negatives others have mentioned: Effective Exceptions
Despite having read the whole page, I still can't find a single reasonable argument against checked exceptions. Most people are instead talking about poor API design, either at some Java classes or at their own classes.
The only scenario where this feature may be annoying is prototiping. This could be solved by adding some mechanism to the language (for instance, some @supresscheckedexceptions annotation). But for regular programming, I think checked exceptions are a good thing.
I've read a lot about exception handling, even if (most of the time) I cannot really say I'm happy or sad about the existence of checked exceptions this is my take : checked exceptions in low-level code(IO, networking, OS, etc) and unchecked exceptions in high-level APIs/application level.
Even if there is not so easy to draw a line between them, I find that it is really annoying/difficult to integrate several APIs/libraries under the same roof without wrapping all the time lots of checked exceptions but on the other hand, sometime it is useful/better to be forced to catch some exception and provide a different one which makes more sense in the current context.
The project I'm working on takes lots of libraries and integrates them under the same API, API which is completely based on unchecked exceptions.This frameworks provides a high-level API which in the beginning was full of checked exceptions and had only several unchecked exceptions(Initialization Exception, ConfigurationException, etc) and I must say was not very friendly. Most of the time you had to catch or re-throw exceptions which you don't know how to handle, or you don't even care(not to be confused with you should ignore exceptions), especially on the client side where a single click could throw 10 possible (checked) exceptions.
The current version(3rd one) uses only unchecked exceptions, and it has a global exception handler which is responsible to handle anything uncaught. The API provides a way to register exception handlers, which will decide if an exception is considered an error(most of the time this is the case) which means log & notify somebody, or it can mean something else - like this exception, AbortException, which means break the current execution thread and don't log any error 'cause it is desired not to. Of course, in order to work out all custom thread must handle the run() method with a try {...} catch(all).
public void run() {
try {
... do something ...
} catch (Throwable throwable) {
ApplicationContext.getExceptionService().handleException("Handle this exception", throwable);
}
}
This is not necessary if you use the WorkerService to schedule jobs(Runnable, Callable, Worker), which handles everything for you.
Of course this is just my opinion, and it might not be the right one, but it looks like a good approach to me. I will see after I will release the project if what I think it is good for me, it is good for others too... :)
ReferenceURL : https://stackoverflow.com/questions/613954/the-case-against-checked-exceptions
'programing' 카테고리의 다른 글
Gradle 빌드를 사용한 IntelliJ IDEA의 Gradle 의존관계 취득 (0) | 2022.06.03 |
---|---|
Tomcat: Tomcat 버전을 실행하는 방법을 알아보려면 어떻게 해야 합니까? (0) | 2022.06.03 |
Vuex의 데이터 프로펠러 상태/게터 통과 방법 (0) | 2022.06.03 |
arrayList.toArray()가 보다 구체적인 유형을 반환하도록 합니다. (0) | 2022.06.03 |
중첩된 데이터와 v-slot이 있는 데이터 테이블 Vuetify: 항목 (0) | 2022.06.03 |