miércoles
oct032012
Tratamiento de Excepciones en Java
Hace un par de semanas publiqué la noticia "Completo curso de Hibernate en español". El curso lo estoy impartiendo actualmente. Mi sorpresa ha sido que todos los alumnos del curso han fallado en el tratamiento de Excepciones. Por ello he escrito un tutorial sobre como hacer el tratamiento de excepciones en Java.
Por si alguien ya sabe de todo ello puede ir directamente al final del tutorial donde con un pequeño truco de Generics se permite relanzar excepciones Checked sin declararlas con throws.
Nota: noticia enviada por logongas
Reader Comments (7)
Muy buena explicación.
Enhorabuena
Esta bien desde el punto de vista de explicar como tratar las excepciones, las opciones que hay y como se comportan, pero para el tono definitorio que se le da, algunas cuestiones quedan ambiguas y otras que no quedan así, deberían serlo.
Por ejemplo, decir que las "checked exception" forman parte del "flujo lógico" es un poco ambiguo/peligroso, ya que la gente acaba pensando que es otra forma de control de flujo, cuando en realidad las excepciones no se han de usar para controlar el flujo lógico. Han de reservarse para situaciones excepcionales y no usarlas, por ejemplo, a manera de goto.
Por otro lado, el concepto de cuando una excepción debería ser checked y cuando no está claro, pero existe el error común, en lo que cae el mismo API de Java, de pensar en las clases específicas de las excepciones, y ahí es donde la cosa se complica.
Definir una FileNotFoundException como "debería ser checked/unchecked" es aventurado puesto que depende de las circusntancias. Ahora mismo tengo en la cabeza dos programas míos en producción donde en un caso es un error si el fichero no existe y otro caso donde "puede pasar y aquí no ha ocurrido nada". Y la SQLException no esta marcada como checked por si hay un error de programación. Lo está por si hay un error de acceso a la BDD, de los tiempos cuando los accesos de red eran mucho menos fiables y como tal, que hubiera un error podía ser considerado "habitual y dentro lo esperado". Sin ser un error de programación.
Tampoco estoy de acuerdo en que definir de forma general excepciones que "no hay que tratarlas", o que "tratarlas pero sólo escribir el log es un error". Todas las excepciones deberían tratarse, lo que pasa es que hay que escoger bien en qué nivel tratarlas y qué hacer con ellas. Y no hay que olvidar que Java se usa para muchas cosas, y un programa de escritorio que "pete" por una excepción no es lo mismo que un programa en el servidor que debe seguir funcionando nomatterwhat o un programa en un movil que si peta te puede dejar colgado el terminal.
El problema esencial es que la definición de cuando/como tratarlas y si es "algo normal" o error de programa, normalmente lo sabe el programa que usa el API, no el API en sí. Pero el que lo decide es el API... pues lío al canto.
Simplemente decir que una guía para explicar lo que hay y como tratarlo me parece estupendo. Intentar ir más alla... no lo pueden hacer de forma coherente ni los propios creadores del API, así que yo no entraría.
De todas formas, todo lo que sea que la gente trate las excepciones algo mejor, es un avance :).
Hola Komorr,
estoy de acuerdo en casi todas las objeciones que has puesto.
Mi intención inicial era poner al principio del tutorial un aviso indicando que esto son simplemente una reglas generales que se pueden saltar según las circunstancias específicas de cada proyecto, pero luego pensé que para gente que no tiene mucha experiencia era mucho mejor dar reglas claras y fáciles de aplicar que ir poniendo excepciones , condicionantes, etc por todo el texto e ir diciendo que "todo depende", ya que al final quedaría como que uno puede hacer lo que quiera porque no hay ninguna regla absoluta de como tratar las excepciones. No quería caer en el "relativismo" que al final justificara cualquier tipo de tratamiento.
También el tutorial está mas pensado para aplicaciones de gestión que para sistemas críticos por lo tanto las reglas que he dado se aplican mejor a ese tipo de software.
En lo único que no estoy de acuerdo contigo es en el tema del "flujo lógico". Para mi una Checked Excepction siempre la he visto como un "goto" para situaciones legítimas pero poco probables. Antiguamente en el lenguaje C se hacía con los famosos valores de retorno de tipo "int" que indicaban el éxito o fallo de la llamada al método.
Saludos.
PD:En clase siempre cuento la diferencia entre el software de una central nuclear y el software de una aplicación de facturación :-)
Hola,
otro apunte al truco del unchecked (requiere de un API propio aparte de java). Logback permite mostrar la traza de la excepción mostrando primero la traza raíz en vez de mostrarla en el orden habitual desde hace un año (la versión 0.9.30).
Detalles aquí: http://jira.qos.ch/browse/LOGBACK-547
Para aquellas personas que dispongan de este mecanismo de log, puede despertar menos reticencias que hacer un throw sospechoso que incluye una clase con código que "no se va a ejecutar nunca pero java no lo sabe".
Por otra parte, he echado en falta algún comentario respecto al tratamiento de excepciones en los "grandes" frameworks, p.ej., en Spring todo es unchecked, y también de los grandes "gurús", dónde hay opiniones para todos los gustos, desde el "el mecanismo de excepciones está ontológicamente mal" hasta "es mejor que el pan con chocolate". Daría algo más de visión de conjunto.
saludos, que me está entrando un apretón y tengo que ayudar a la reforestación nacional plantando pinos.
p.d. y off-topic: siempre que veo lo que sea relacionado con Hibernate y ORMs me acuerdo del artículo http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx , de Ted Neward
Yo siempre he leído que las excepciones son para situaciones excepcionales, y no deben usarse para flujo. Siempre. Por rendimiento, por ejemplo ya que crear una excepción no es gratis, por evitar el mismo problema de los gotos, múltiples return... No es el mismo caso que los valores de retorno de un método, por que ahí no hay "saltos mágicos", si no una comprobación de un valor, fácil de seguir.
No quiero decir que solo se usen para cuando algo "se ha roto", pero lo normal en un programa debería ser no lanzar excepciones. Por supuesto que ya sabemos que al "parsear" datos introducidos etc. pueden darse casos, pero es, al fin y al cabo, un error en la introducción.
Yo me refiero a que está mal usarlos a modo de goto, en vez de hacer un if p.e., para casos normales, que lo he visto hacer. Me refiero a poner la lógica en diferentes catch y decidir que lógica ejecutar lanzando una u otra excepción, dependiendo de los valores esperados.
@Komorr
Te pongo por ejemplo es "JSR 303: Bean Validation" e Hibernate. Se lanza la excepción "Unchecked" javax.validation.ConstraintViolationException para indicar que alguna valiación ha fallado. Y estas validaciones se usan para comprobar si los datos escritos por el usuario son correctos.
Perfectamente el método "save" de Hibernate podría haber retornado una lista de restricciones que han fallado, pero prefirieron lanzar una excepción.
Es el mismo caso que yo he descrito, y uno de los casos "excepcionales" que son esperados y habituales. :)