web-dev-qa-db-ja.com

キャッチが実際に何もキャッチしない場合

最近、データベースに保存された不良データが原因でプログラムがクラッシュしました。私はこれを防ぐためにキャッチがあると思ったので、これは私を混乱させました。

次のコードの目的は、従業員のバッジ番号を比較して並べ替えることです。エラーが発生した場合は、-1を返して兵士を返します-数千のバッジ番号のいずれかが間違っているため、停止しないでください。

public int compare(Employee t, Employee t1) {
    Integer returnValue = -1;
    try {
        Integer tb = Integer.parseInt(t.getBadgeNumber());
        Integer t1b = Integer.parseInt(t1.getBadgeNumber());
        returnValue = tb.compareTo(t1b);
    } catch (Exception e) {
        returnValue = -1;//useless statement, I know.
    }
    return returnValue;
}

不正なバッジ番号がヒットした場合(この場合はt)、「Java.lang.IllegalArgumentException:比較メソッドがその一般契約に違反しています!」 catchに-1を返す代わりにエラー。

ここでのキャッチについて理解できないことがありますか?

完全なスタックトレース:

16-May-2018 14:28:53.496 SEVERE [http-nio-8084-exec-601] org.Apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [RequestServlet] in context with path [/AppearanceRequest] threw exception
 Java.lang.IllegalArgumentException: Comparison method violates its general contract!
at Java.util.TimSort.mergeHi(TimSort.Java:868)
at Java.util.TimSort.mergeAt(TimSort.Java:485)
at Java.util.TimSort.mergeForceCollapse(TimSort.Java:426)
at Java.util.TimSort.sort(TimSort.Java:223)
at Java.util.TimSort.sort(TimSort.Java:173)
at Java.util.Arrays.sort(Arrays.Java:659)
at Java.util.Collections.sort(Collections.Java:217)
at org.bcso.com.appearancerequest.html.NotifierHTML.getHTML(NotifierHTML.Java:363)
at org.bcso.com.appearancerequest.AppearanceRequestServlet.processRequest(AppearanceRequestServlet.Java:96)
at org.bcso.com.appearancerequest.AppearanceRequestServlet.doGet(AppearanceRequestServlet.Java:565)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:618)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:725)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:301)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:206)
at org.Apache.Tomcat.websocket.server.WsFilter.doFilter(WsFilter.Java:52)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:239)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:206)
at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.Java:393)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:239)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:206)
at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:219)
at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:106)
at org.Apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.Java:503)
at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:136)
at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:74)
at org.Apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.Java:610)
at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:88)
at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:516)
at org.Apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.Java:1015)
at org.Apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.Java:652)
at org.Apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.Java:222)
at org.Apache.Tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.Java:1575)
at org.Apache.Tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.Java:1533)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1145)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:615)
at Java.lang.Thread.run(Thread.Java:745)

呼び出しコード:

    List<Employee> employeeList = DatabaseUtil.getEmployees();
    Collections.sort(employeeList, new BadgeComparator());
52
Bob Stout

例外(それが何であれ)wascatch (Exception e)によってキャッチされました。この例外をログに記録しなかったため、それが何であるかわかりません。何が実際に起こったかを知るために、何らかの形でログに記録する必要があります。

この問題は、-1を返すときに発生します。これにより、Javaの現在のソートアルゴリズムがキャッチすることがある、一貫性のない順序付けの可能性が考慮されます。要するに、エラー時に-1を返すということは、a < bb < aの両方がtrueであるとアサートしていることを意味します。両方の場合で例外がキャッチされるからです。これは論理的に間違っています。ソートアルゴリズムはこれを検出し、IllegalArgumentExceptionをスローします。 compareメソッドは、スタックトレースではnotであることに注意してください。 Collections.sortの呼び出しです。

例外をログに記録することに加えて、プログラムの比較ステップに進む前に例外を処理します。文字列を整数として解析する必要がある場合は、Employeeオブジェクトを作成するときに実行して、プログラムの並べ替え手順に進む前に検証が行われるようにします。 Comparatorはデータを検証する必要はありません。データを比較するだけです。

142
rgettman

説明

Java.lang.IllegalArgumentException:比較メソッド違反その一般契約

try内から例外はスローされません。それがつかまえられない理由です。例外は、TimSortクラスを使用するNotifierHTML.Java:363を呼び出すコード内のCollection#sortから発生します。その後、TimSort.Java:868メソッドによってTimSort#mergeHiから例外がスローされます。

Comparator#compareメソッドの実装が間違っていることがわかります。 documentation で説明されているように、契約に違反しています。

2つの引数の順序を比較します。 整数、ゼロ、または整数を返します。最初の引数はより小等しい、またはより大きい秒。

実装者必ず確認sgn(x.compareTo(y)) == -sgn(y.compareTo(x))すべてのxおよびyに対して。 (これは、x.compareTo(y)が例外をスローする場合、y.compareTo(x)が例外をスローする必要があることを意味します。)

実装者また確認する必要があるリレーションがtransitiveであること:(x.compareTo(y) > 0 && y.compareTo(z) > 0)x.compareTo(z) > 0を意味します。

最後に、実装者必ず確認x.compareTo(y) == 0は、すべてのzについて、sgn(x.compareTo(z)) == sgn(y.compareTo(z))を意味します。

実装がこれらの要件の1つに違反しており、メソッドがそれを検出しました。


問題の原因

問題は、エラーが発生した場合に-1を返すことです。 firstsecondの2つの値があるとします。そして、そのうちの少なくとも1つが例外を引き起こします。

したがって、firstsecondを比較したい場合は、-1を取得します。

compare(first, second) -> -1

これは、firstsmallersecondよりも大きいことを意味します。しかし、他の方法で比較すると、-1も得られます:

compare(second, first) -> -1

両方のバリアントで例外がスローされるため、return -1;につながります。しかし、これはあなたのcompareメソッドが言うことを意味します:

first < second
second < first

両方とも論理的に正しくなく、契約に違反しています。


溶液

解析できないコンテンツが配置される順序を正しく定義する必要があります。たとえば、それが常にどんな数字よりも小さいことを定義してみましょう。だから欲しい

text < number

両方が解析できない場合はどうしますか?それらは等しいと言うことができ、辞書式に比較することができます。シンプルに保ち、任意の2つのテキストは等しいと見なされます。

text = text

どの引数が解析不能であるかをチェックし、正しい値を返すことでこれを実装します:

@Override
public int compare(Employee first, Employee second) {
    Integer firstValue;
    Integer secondValue;
    try {
        firstValue = Integer.parseInt(first.getBadgeNumber());
    } catch (NumberFormatException e) {
        // Could not parse, set null as indicator
        firstValue = null;
    }
    try {
        secondValue = Integer.parseInt(second.getBadgeNumber());
    } catch (NumberFormatException e) {
        // Could not parse, set null as indicator
        secondValue = null;
    }

    if (firstValue == null && secondValue != null) {
        // text < number
        return -1;
    }
    if (firstValue != null && secondValue == null) {
        // number > text
        return 1;
    }
    if (firstValue == null && secondValue == null) {
        // text = text
        return 0;
    }

    // Both are numbers
    return Integer.compare(firstValue, secondValue);
}

コメントで示唆されているように、カスタムComparatorクラス全体を、同じコンパレータを生成する次のステートメントで置き換えることができます。

Comparator<Employee> comp = Comparator.nullsLast(
    Comparator.comparing(e -> tryParseInteger(e.getBadgeNumber())));

次のようなtryParseIntegerメソッドとともに:

public static Integer tryParseInteger(String text) {
    try {
        return Integer.parseInt(text);
    } catch (NumberFormatException e) {
        return null;
    }
}
52
Zabuza

これはそうではありませんが、 Throwable インスタンスをスローしてキャッチでき、例外とは別に Errors があることに注意してください。それらをキャッチすることは可能ですが、それらが発生した場合、それ以上の作業を行うことはできません。

したがって、try-catchは、エラーまたは例外以外のThrowableをキャッチしませんでした。

public static void main(String[] args) {

    try {
        throw new Error("test exception try-catch");
    } catch (Throwable e) {
        System.out.println("Error caught in throwable catch");
    }

    try {
        throw new Error("test exception try-catch");
    } catch (Exception e) {
        System.out.println("Error caught in exception catch");
    }
}

結果は次のとおりです。

Error caught in throwable catch
Exception in thread "main" Java.lang.Error: test exception try-catch
    at ...
4
Dariusz

この例外は、ここで貼り付けたcompareメソッドではスローされません。スタックトレースを確認してください。 compare呼び出しはありません。

2
Antoniossss

TimSort.mergeHi()を明示的に呼び出したときに、内部で呼び出されたCollections.sort()から例外がスローされます。

java.util.TimSort.mergeHi(TimSort.Java:868)で

Catchステートメントをsort()の周りに移動することもできますが、結果としてソートは実行されないか、完全ではありません。したがって、それは良い考えではないようです。
長い話:compareTo()コントラクトに違反しないでください。これ以上発生しない例外をキャッチする必要はありません。

0
davidxxx