Vienen en dos sabores: Descendienden de Exception o de RuntimeException. Las primeras deben ser consideradas obligatoriamente por el desarrollador; las segundas no necesariamente. Por qué los dos sabores? cuál utilizar?
La intención original de los creadores de Java parece haber sido que los programas utilicen exclusivamente las “Checked Exceptions”, es decir, las que descienden de Exception. Las aplicaciones que lo requiriesen también deberían derivar sus propias excepciones de Exception (o quizá de alguna de las excepciones existentes, por ejemplo, MiNetworkDriverException pordría derivar de IOException.)
De manera un tanto dubitativa, Sun también proporcionó una jerarquía paralela de excepciones que no obligarían al programador a capturarlas. Estas excepciones tenían como origen una serie de “errores de programación” tales como:
String x[] = new String[5];
x[5] = “hi!”;
Como es sabido, esto produce una excepción (al estar los índices válidos en el rango de cero a cuatro); esto es causado por un error evidente en el código y la única solución consiste en corregirlo; esto es, no tiene sentido “capturar una excepción” pues no habría nada que hacer al respecto.
De acuerdo con la lógica de Sun, otros son los problemas que se pueden suscitar en la ejecución del programa, y que el código debería considerar. El caso más conocido corresponde a los errores de I/O con archivos de disco. Por ejemplo, los archivos podrían ser eliminados repentinamente, o podrían tener permisos inapropiados, o el disco podría fallar o hacerse inaccesible, etc. Dado que estos eventos están fuera de las manos del programador (o mejor dicho, del proceso en ejecución), la solución no pasa por “corregir” el programa para que no ocurran, sino en forzarlo a que esté preparado para hacerles frente (en el bloque “catch”.) Esto es, ciertas invocaciones en ciertos objetos (como los de I/O) generan excepciones de procesamiento obligatorio (checked excepcions.)
Esto suena bien; quizá, demasiado bien. Con este criterio se podría evitar (desde el momento de compilación) una gran cantidad de “bugs” causados por programas que no consideran una serie de eventos externos.
La perfección es divina
De acuerdo a la lógica anteriormente descrita, los programas libres de errores tontos (esos que generan “unchecked exceptions”) deberán capturar las excepciones obligatorias. Un primer problema ampliamente comentado corresponde a la polución del código resultante con sentencias de captura (como cualquier persona que ha visto los tutoriales de JDBC puede dar fe.) Los amantes de la perfección sostienen que este es un mal menor, y en cualquier caso uno siempre puede “propagar la excepción” a un nivel superior, el cual podría capturar las excepciones de manera más centralizada y menos confusa.
El segundo problema radica en qué hacer con la excepción capturada, lo cual es el propósito de todo este asunto. Los ejemplos “académicos” proponen cosas como “reintentar la operación”, “solicitar una alternativa al usuario”, “notificar y esperar a que el usuario solucione el problema”, etc. Lamentablemente en la práctica esto casi nunca ocurre, y la excepción suele terminar convertida en el clásico:
ex.printStacktrace();
o propagada hasta el famoso main con Exception:
public static void main(String[] args) throws Exception {…}
El ex.printStacktrace() no es tan malo después de todo: es importante registrar las excepciones para posterior análisis; sin embargo, esto lo suelen hacer los frameworks de manera automática.
Pragmatismo al rescate
En qué caso tiene sentido capturar las excepciones? obviamente para hacer algo al respecto de su ocurrencia. En el resto de ocasiones simplemente es inutil. Lamentablemente Sun desarrolló sus APIs con la intención de “proteger” a los programadores de sus “descuidos típicos”, pecando por exceso de celo. Pareciera que también primó un criterio subjetivo de “importancia de la excepción”; por ejemplo, SimpleDateFormat genera una “checked Exception” cuando hay un error en el parsing de una fecha; al igual que en el caso de los índices del array arriba mencionado, el texto de la fecha está enteramente en las manos del programador:
new SimpleDateFormat(“dd/MM/yyyy”).parse(“31/31/2009″); // requiere try … catch!
En la práctica, la mayoría del código no requiere capturar las excepciones pues no tiene nada que hacer con éstas. La propagación es la norma, y las humildes excepciones “unchecked” son el mejor mecanismo para implementarla. De allí que frameworks como Spring tienen como parte de sus “valores agregados” la transformación de “checked exceptions” en “RuntimeExceptions” a ser procesadas sólo si el desarrollador tiene algo que hacer al respecto.
Alternativa para las Checked Exceptions
A mi entender, el problema de las Checked Exceptions se genera en la asunción de un implementador de un método cuando asume que sus excepciones deberían ser capturadas por quien lo invoca. Esto sencillamente esta mal. No hay manera de predecir si quien invoca el código podrá sacar partido de la captura de las excepciones.
Si este conocimiento sólo lo tiene el programador que invoca el código en cuestión, es aquí donde se debería especificar que estamos interesados en capturar exceptiones. Por ejemplo, si invocaremos un metodo en un punto en el que debemos o podemos hacer frente a sus excepciones, el lenguaje podría proporcionar una construcción “try_all” (análoga a “try”) en cuyo interior todas las excepciones actúen como “checked exception” y nos fuerce a capturarlas:
// en este punto del programa se puede hacer algo con los errores..
try_all { // bloque de captura
obj.metodo(); // el metodo (o varios métodos) que genera excepciones
} // agregar bloques catch() {} para que compile
esto es, la decisión del momento en que se procesarán las excepciones se traslada al código que hace la llamada.
0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.