53 votes

Quand la capture n'attrape rien

J'ai eu un plantage du programme à cause de mauvaises données stockées dans une base de données récemment. Ce qui me confond, parce que je pensais que j'avais une prise pour l'en empêcher.

L'intention de le code suivant est à comparer employé numéros de badge et de les trier. Si il y a une erreur, renvoie -1 et soldat sur -- ne vous arrêtez pas parce que l'un de plusieurs milliers de numéros de badge est faux:

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;
}

Lorsque le mauvais numéro de badge frappé (comme t dans ce cas), j'ai eu un "java.lang.IllegalArgumentException: Comparaison de la méthode est contraire ses conditions générales du contrat!" erreur au lieu de retourner le -1 dans le catch.

Que n'ai-je pas comprendre au sujet de la capture ici?

La pleine stacktrace:

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)

Le code d'appel:

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

143voto

rgettman Points 74908

L'exception (quelle qu'elle soit) a été pris en catch (Exception e). Vous n'avez pas le journal de cette exception, donc vous ne savez pas ce que c'était. Vous devez vous connecter en quelque sorte, donc vous savez ce qui est vraiment arrivé.

Le problème se produit lorsque vous revenez -1. Cela permet la possibilité de divergences dans l'ordre, Java actuel de l'algorithme de tri parfois des captures. En bref, en revenant -1 sur une erreur signifie que vous êtes en affirmant que les deux a < b et b < a sont vraies, parce que l'exception d'être pris dans les deux cas. C'est logiquement incorrecte. L'algorithme de tri le détecte et déclenche l' IllegalArgumentException. Notez que l' compare méthode n'est pas dans votre trace de la pile; c'est l'appel à l' Collections.sort.

En plus de l'enregistrement de l'exception, la traiter avant même d'arriver à la comparaison de l'étape dans votre programme. Si vous devez analyser la chaîne en un entier, le faire lors de la création de l' Employee objets, de sorte que la validation se produit avant même d'arriver à l'étape de tri dans votre programme. Un Comparator ne devriez pas avoir à valider les données; il doit seulement comparer les données.

53voto

Zabuza Points 11045

Explication

java.lang.IllegalArgumentException: méthode de Comparaison de la viole sa générales du contrat!

L'exception n'est pas générée à partir de votre try. C'est pourquoi il n'est pas pris. L'exception vient de NotifierHTML.java:363 dans votre code où vous appelez Collection#sort qui utilise un TimSort classe. L'exception est alors projeté de TimSort.java:868 par l' TimSort#mergeHi méthode.

Il vous dit que votre mise en œuvre de l' Comparator#compare méthode est mauvaise. Il viole le contrat, comme l'a expliqué dans sa documentation:

Compare ses deux arguments à la commande. Retourne un négatif entier, zéro, ou un positif entier comme le premier argument est inférieure, égale, ou supérieure à la seconde.

Le fournisseur doit s'assurer sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) tous x et y. (Ce qui implique que x.compareTo(y) doit lancer une exception iff y.compareTo(x) déclenche une exception.)

Le réalisateur doit également s'assurer que la relation est transitive: (x.compareTo(y) > 0 && y.compareTo(z) > 0) implique x.compareTo(z) > 0.

Enfin, le responsable doit s'assurer que l' x.compareTo(y) == 0 implique qu' sgn(x.compareTo(z)) == sgn(y.compareTo(z)), pour tous les z.

Votre mise en œuvre contrevient à l'une de ces exigences et la méthode détecté.


La Source du problème

Le problème, c'est que vous revenez -1 si une erreur se produit. Supposons que vous avez deux valeurs first et second. Et qu'au moins l'un d'entre eux va provoquer l'exception.

Donc, si vous voulez comparer first avec second, vous bénéficiez d' -1:

compare(first, second) -> -1

Ce qui signifie qu' first est plus petit que second. Mais si on le compare à celui de l'autre façon, vous obtenez -1 trop:

compare(second, first) -> -1

Car l'exception est levée dans les deux variantes, ce qui conduit à votre return -1;. Mais cela signifie que votre compare méthode dit:

first < second
second < first

Les deux en même temps, ce qui est logiquement incorrecte et constitue une violation du contrat.


Solution

Vous avez besoin afin de bien définir où dans votre commande de unparsable contenu est au. Pour exemple, nous allons nous définissons qu'il est toujours plus petite que n'importe quel nombre. Nous voulons donc

text < number

Que faisons-nous si les deux sont unparsable? On pourrait dire qu'ils sont égaux, on pourrait comparer le vocabulaire. Nous allons garder les choses simples et dire que les deux textes sont considérés comme égaux:

text = text

Nous le mettre en vérifier quels sont les arguments sont unparseable puis de retourner la valeur correcte:

@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);
}

Comme évoqué dans les commentaires vous pouvez remplacer l'ensemble de votre coutume Comparator de la classe par la déclaration suivante qui génère le même élément de Comparaison:

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

Avec un tryParseInteger méthode comme ceci:

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

4voto

Dariusz Points 8058

Alors que ce n'est pas le cas, n'oubliez pas que vous pouvez lancer et à attraper Throwable cas, et sauf Exceptions il y a des Erreurs. Leur pêche est possible, mais quand ils se produisent, son peu probable que tout autre travail qui peut être fait.

Si votre try-catch n'aurait pas pris une Erreur ou d'une Throwable autres que l'Exception.

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");
    }
}

Ce qui aura pour résultat:

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

2voto

Antoniossss Points 2477

Cette exception n'est pas levée dans la méthode de comparaison que vous avez collée ici. Vérifiez le stacktrace. Il n'y a pas d'appel compare dedans.

0voto

davidh Points 107

L'exception est générée à partir d' TimSort.mergeHi() invoquée à l'interne, comme vous l'avez explicitement invoqué Collections.sort() :

à java.util.TimSort.mergeHi(TimSort.java:868)

Vous pouvez déplacer l'instruction catch autour de sort() , mais comme une conséquence de la sorte ne sera pas effectuée ou être pas complet. Donc, il ne semble pas être une bonne idée.
Longue histoire courte : ne pas violer l' compareTo() contrat et vous n'auriez pas besoin d'attraper toutes les exceptions qui ne se produira pas plus longtemps.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X