La semana pasada tuvimos
una discusión bastante interesante sobre excepciones cheked y uncheked. Al final de ella seguía habiendo personas que no veían ningún problema en las excepciones cheked y que estaban a favor de usarlas en escenarios donde muchos pensamos que no se deberían de emplear.
Me ha parecido que una buena forma de profundizar en este tema y reflexionar sobre él sería analizar en detalle lo que ha hecho el lenguaje de programación que ha nacido como una evolución de Java: C#. Para crear este lenguaje, aunque no se reconozca oficialmente, se tomó todo lo que funcionaba de Java (la gran mayoría de las cosas) y se quitó y mejoró lo que no funcionaba adecuadamente. Exactamente lo mismo que hizo en su día Java con C++.
Este lenguaje de programación no tiene excepciones cheked. En palabras de su principal responsable, Anders Hejlsberg, no las incorporaron en el lenguaje porque no estaba claro que fuesen beneficiosas. Efectivamente, resolvían una serie de problemas. Pero creaban otros problemas y al final no estaba claro si "el mundo es mejor con o sin ellas, simplemente es diferente".
Dos son los principales motivos que llevaron al equipo que diseñó C # a no incorporar estas excepciones en el lenguaje:
- Escalabilidad de las excepciones: Imaginemos que un conjunto de desarrolladores está diseñando un componente complejo. Imaginemos que el código de cada uno de los desarrolladores puede lanzar cinco excepciones (por poner un número cualquiera). Imaginemos que un componente está siendo desarrollado por cinco desarrolladores. Cuando junten su código tienen 25 excepciones. Ahora supongamos que el componente se junta con otros cinco componentes para crear un subsistema. 125 excepciones. Ese subsistema se une a otros cinco subsistemas. 625 excepciones.
El ejemplo es un poco exagerado. Pero creo que se coge la idea. Además, no es necesario que las excepciones sean excepciones que hayamos definido nosotros. Si se usan en un mismo proyecto Hibernate, Struts, JasperReports... puedes obtener un efecto similar. Acabas con una jerarquía de excepciones monstruosa. Y esto es probable que lleve a los desarrolladores a hacer cosas como "throws Exception" en todos sus métodos para no listar la interminable lista de excepciones. En ese momento todo el propósito de las excepciones cheked ha sido completamente destruido y el mecanismo de gestión deja de tener sentido.
- Versionabilidad de las excepciones: En Java un método no está definido sólo por su tipo de retorno, su nombre y los parámetros que toma. También por las excepciones que lanza. Supongamos que tenemos una determinada interfaz de una librería que puede lanzar las excepciones A y B. En una versión posterior de la librería añadimos nueva funcionalidad y respetamos la definición de todos los métodos de la interfaz, sólo que ahora (por culpa de la nueva funcionalidad) uno de ellos lanza una excepción C.
Si añades si esa excepción a la interfaz estará rompiendo todo el código cliente existente. Habrás cambiado la interfaz. Si no la añades irás en contra de la filosofía de las excepciones cheked. Observa que, en este caso, podrías añadir cuantas excepciones uncheked quisieses a la interfaz sin romper el código cliente.
Además, Anders Hejlsberg a firma de las excepciones cheked que:
- No te obligan a gestionar la excepción: Sólo te obligan a "asentir" que se puede lanzar: try{...} catch (AlgunaExcepcion){}. Este código "reconoce" que se puede lanzar la excepción AlgunaExcepcion. Pero no la gestiona. ¿Te resulta familiar ese código? ¿Lo has visto alguna vez?
- Son "dictatoriales": Yo conozco mi aplicación. Yo sé lo qué está haciendo. Yo se qué filosofía de gestión de excepciones estamos usando en el proyecto. ¿Por qué el diseñador dictatorial del API que estoy usando me está obligando a capturar excepciones que no me interesan?. Si las excepciones fuesen uncheked yo podría capturarlas o no capturarlas según se adecuase mis necesidades en cada caso.
- En la mayor parte de los escenarios lo importante es protegerse de las excepciones, no gestionarlas: es más, afirma que la relación entre try-finallys y try-catch en un buen código es 10 a 1. Efectivamente, me interesa protegerme de las excepciones para que mi código siga en un estado consistente. Pero la mayor parte de las veces no la voy a gestionar. Ya se gestionarán en algún punto cercano a la interfaz del usuario "más arriba".
Indudablemente, podemos afirmar que Anders Hejlsberg tiene que defender las decisiones de diseño que tomaron para crear C#. Aún así, sus argumentos son bastante respetables. Y hay una cosa que es innegable: para crear C# estudiaron en profundidad lo que funcionaba bien y lo que no funcionaba en Java. Y ya sabemos la decisión que tomaron respecto a este tema.
Por otro lado, como siempre, un buen programador va a escribir buen código con o sin excepciones cheked. El problema con esta característica del lenguaje, como con casi cualquier otra, es el uso que puede acabar haciendo el programador perezoso harto de gestionar tanta excepción: throws Exception o try{...} catch (Excepcion){}.
¿Opiniones al respecto?