java - ejemplo - manual de programacion android pdf
Cuando la captura en realidad no captura nada (5)
Explicación
java.lang.IllegalArgumentException: ¡El método de comparación viola su contrato general!
La excepción no es lanzada desde tu
try
.
Es por eso que no está atrapado.
La excepción proviene de
NotifierHTML.java:363
en su código en el que llama a
Collection#sort
que usa una clase
TimSort
.
La excepción es lanzada desde
TimSort.java:868
por el método
TimSort#mergeHi
.
Le indica que su implementación del método de
Comparator#compare
es incorrecta.
Viola el contrato, tal como se explica en su
documentation
:
Compara sus dos argumentos por orden. Devuelve un entero negativo , cero o un entero positivo ya que el primer argumento es menor que , igual o mayor que el segundo.
El implementador debe asegurarse de que
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
para todas lasx
yy
. (Esto implica quex.compareTo(y)
debe lanzar una excepción siy.compareTo(x)
lanza una excepción).El implementador también debe asegurarse de que la relación sea transitiva :
(x.compareTo(y) > 0 && y.compareTo(z) > 0)
implicax.compareTo(z) > 0
.Finalmente, el implementador debe asegurarse de que
x.compareTo(y) == 0
implica quesgn(x.compareTo(z)) == sgn(y.compareTo(z))
, para todas lasz
.
Su implementación viola uno de esos requisitos y el método lo detectó.
Fuente del problema.
El problema es que devuelve
-1
si se produce un error.
Supongamos que tienes dos valores
first
y
second
.
Y que al menos uno de ellos provocará la excepción.
Entonces, si quieres comparar
first
con
second
, obtienes
-1
:
compare(first, second) -> -1
Lo que significa que
first
es
más pequeño
que el
second
.
Pero si lo comparas de la otra manera, obtienes
-1
también:
compare(second, first) -> -1
Debido a que la excepción se produce en ambas variantes, lo que lleva a su
return -1;
.
Pero esto significa que su método de
compare
dice:
first < second
second < first
Ambos al mismo tiempo, lo cual es lógicamente incorrecto y viola el contrato.
Solución
Debe definir correctamente dónde se ubica en su pedido el contenido no analizable. Por ejemplo, definamos que siempre es más pequeño que cualquier número. Por eso queremos
text < number
¿Qué hacemos si ambos son imparables? Podríamos decir que son iguales, podríamos compararlos lexicográficos. Mantengámoslo simple y digamos que dos textos se consideran iguales:
text = text
Implementamos esto verificando cuáles de los argumentos son imposibles de analizar y luego devolvemos el valor correcto:
@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);
}
Como se indicó en los comentarios, puede reemplazar toda su clase personalizada de
Comparator
por la siguiente declaración que genera el mismo Comparator:
Comparator<Employee> comp = Comparator.nullsLast(
Comparator.comparing(e -> tryParseInteger(e.getBadgeNumber())));
Junto con un método
tryParseInteger
como este:
public static Integer tryParseInteger(String text) {
try {
return Integer.parseInt(text);
} catch (NumberFormatException e) {
return null;
}
}
Esta pregunta ya tiene una respuesta aquí:
- “¡El método de comparación viola su contrato general!” 11 respuestas
Tuve un bloqueo del programa debido a la mala información almacenada en una base de datos recientemente. Esto me confundió, porque pensé que tenía una trampa para evitar esto.
La intención del siguiente código es comparar los números de credencial de los empleados y ordenarlos. Si hay un error, devuelva -1 y el soldado está encendido. No se detenga porque uno de los miles de números de distintivos es incorrecto:
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;
}
Cuando se produjo el número de placa incorrecta (como t en este caso), obtuve una "java.lang.IllegalArgumentException: ¡El método de comparación viola su contrato general!" Error en lugar de devolver el -1 en la captura.
¿Qué no entiendo de la captura aquí?
El stacktrace completo:
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)
El código de llamada:
List<Employee> employeeList = DatabaseUtil.getEmployees();
Collections.sort(employeeList, new BadgeComparator());
Esa excepción no se lanza en el método de
comparación
que pegaste aquí.
Comprueba el stacktrace.
No hay ninguna llamada de
compare
en ella.
La excepción (cualquiera que sea)
fue
capturada por
catch (Exception e)
.
Usted no registró esta excepción, por lo que no sabe lo que era.
Deberías registrarlo de alguna manera para que sepas lo que realmente sucedió.
El problema ocurre cuando devuelves
-1
.
Esto permite la posibilidad de un ordenamiento incoherente, que el algoritmo de clasificación actual de Java a veces detecta.
En resumen, devolver
-1
en un error significa que está afirmando que tanto
a < b
como
b < a
son verdaderas, porque la excepción se detectará en ambos casos.
Esto es lógicamente incorrecto.
El algoritmo de clasificación lo detecta y lanza la
IllegalArgumentException
.
Tenga en cuenta que el método de
compare
no
está en su seguimiento de pila;
Es la llamada a
Collections.sort
.
Además de registrar la excepción, manéjela antes de llegar al paso de comparación en su programa.
Si tiene que analizar la cadena como un entero, hágalo al crear los objetos de
Employee
, de modo que la validación ocurra antes de que incluso llegue al paso de clasificación en su programa.
Un
Comparator
no debería tener que validar los datos;
Solo debe comparar los datos.
La excepción es lanzada desde
TimSort.mergeHi()
invocado internamente como invocó explícitamente
Collections.sort()
:
en java.util.TimSort.mergeHi (TimSort.java:868)
Podría mover la instrucción catch alrededor de
sort()
pero, como consecuencia, la clasificación no se realizará o no estará completa.
Así que no parece ser una buena idea.
Larga historia corta: no viole el contrato
compareTo()
y no tendrá que atrapar ninguna excepción que ya no suceda.
Si bien este no es el caso, recuerde que puede lanzar y capturar instancias de Throwable y, aparte de las excepciones, hay Errors . Captarlos es posible, aunque cuando ocurren es poco probable que se pueda hacer más trabajo.
Por lo tanto, su intento de captura no habría atrapado un error o cualquier otro Throwable que no sea Excepción.
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");
}
}
Lo que resultará en:
Error caught in throwable catch
Exception in thread "main" java.lang.Error: test exception try-catch
at ...