Когда catch на самом деле ничего не ловит [дубликат]
этот вопрос уже есть ответ здесь:
"Метод сравнения нарушает его генеральный контракт!"
10 ответов
у меня был сбой программы из-за плохих данных, хранящихся в базе данных. Это смутило меня, потому что я думал, что у меня есть уловка, чтобы предотвратить это.
целью следующего кода является сравнение сотрудников номера значков и сортировать их. Если есть ошибка, верните -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.ленг.IllegalArgumentException: метод сравнения нарушает его генеральный контракт!"ошибки вместо того, чтобы возвращать -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());
5 ответов:
исключение (что бы это ни было) был поймал
catch (Exception e). Вы не регистрировали это исключение, поэтому вы не знаете, что это было. Вы должны войти в него как-то, чтобы вы знали, что на самом деле произошло.проблема возникает при возврате
-1. Это допускает возможность несогласованного упорядочения, которое иногда улавливает текущий алгоритм сортировки Java. Короче говоря, возвращение-1на ошибку означает, что вы утверждаете, что обаa < bиb < aнесколько правда, потому что исключение будет поймано в обоих случаях. Это логически неверно. Алгоритм сортировки обнаруживает это и выдаетIllegalArgumentException. Обратите внимание, чтоcompareметод не в трассировке стека; это вызовCollections.sort.в дополнение к регистрации исключения, обрабатывать его, прежде чем даже перейти к шагу сравнения в вашей программе. Если вам нужно разобрать строку как целое число, сделайте это при создании
Employeeобъекты, так что проверка происходит до того, как вы даже доберетесь до шага сортировки в своей программе. АComparatorне нужно проверять данные; он должен только сравнивать данные.
объяснение
java.ленг.IllegalArgumentException: метод сравнения нарушение его общие контракт!
исключение не выбрасывается из вашего
try. Вот почему он не пойман. Исключение происходит отNotifierHTML.java:363в вашем коде, где вы звонитеCollection#sortиспользуетTimSortкласса. Исключение затем выбрасывается изTimSort.java:868наTimSort#mergeHiметод.он говорит вам, что ваша реализация
Comparator#compareметод неверен. Это нарушает договор, как поясняется в его документация:сравнивает свои два аргумента для порядка. Возвращает отрицательный целое число, ноль или положительное целое число в качестве первого аргумента является меньше,равна или больше второй.
конструктор должны обеспечьте
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))для всехxиy. (Это означает, чтоx.compareTo(y)должен бросить исключение iffy.compareTo(x)выдает исключение.)конструктор также должны обеспечить что это транзитивное:
(x.compareTo(y) > 0 && y.compareTo(z) > 0)подразумеваетx.compareTo(z) > 0.наконец, конструктор должны обеспечивать это
x.compareTo(y) == 0означает, чтоsgn(x.compareTo(z)) == sgn(y.compareTo(z))для всехz.ваша реализация нарушает одно из этих требований и метод обнаружили, что.
источник проблемы
проблема в том, что вы вернете
-1при возникновении ошибки. Предположим, у вас есть два значенияfirstиsecond. И что хотя бы один из них спровоцирует исключение.так что если вы хотите сравнить
firstСsecondвы получаете-1:compare(first, second) -> -1что означает
firstи меньше чемsecond. Но если вы сравниваете его по-другому вы получаете-1тоже:compare(second, first) -> -1потому что исключение выбрасывается в обоих вариантах, что приводит к вашему
return -1;. Но это означает вашcompareметод говорит:first < second second < firstоба одновременно, что логически неверно и нарушает договор.
решение
вам нужно правильно определить, где в вашем заказе размещается несопоставимый контент. Например, давайте определим, что это всегда меньше, чем любое число. Поэтому мы хотим
text < numberчто нам делать, если и то и другое непростительно? Мы могли бы сказать, что они равны, мы могли бы сравнить их лексикографически. Давайте просто скажем, что любые два текста считаются равными:
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; } }
хотя это не так, помните, что вы можете бросить и поймать Throwable экземпляры, и кроме исключений есть ошибки. Поймать их можно, хотя когда они происходят его вряд ли можно сделать какую-либо дальнейшую работу.
таким образом, ваш try-catch не поймал бы ошибку или какой-либо бросок, кроме исключения.
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 ...
это исключение не бросается в сравнить метод, который вы вставили сюда. Проверка трассировки стека. Нет никакого
compareзвонок в ней.
исключение из
TimSort.mergeHi()вызывается внутренне, как вы явно вызываетсяCollections.sort():на java.утиль.TimSort.mergeHi(TimSort.java: 868)
вы можете переместить оператор catch вокруг
sort()но как следствие сортировка не будет выполнена или не будет полной. Так что, похоже, это не очень хорошая идея.
Короче говоря: не нарушайтеcompareTo()контракт, и вам не нужно будет ловить любое исключение, которое не произойдет длиннее.
Comments