Pildorita de POO y Java: Tratamiento de excepciones

Como comenté en la anterior pildorita de esta serie, terminábamos ya con la parte de Programación Orientada a Objetos y sólo quedaban ya los temas ajenos a este paradigma, más concretos de Java.

Algo ajeno a la POO pero fundamental a la hora de programar ya no sólo en Java, sino en cualquier lenguaje de programación, es el tratamiento de errores, ya que todos los programas tienen errores.

Si quieres dedicarte al mundo del desarrollo es algo que más vale que asimiles cuanto antes: Tu código siempre tendrá algún error, da igual lo bueno que seas. No obstante, un buen desarrollador controlará el máximo de errores posibles y definirá comportamientos para tales incontingencias.

Se escapa al propósito de estas pildoritas y daría para otra serie de pildoras, poder aprender todos los conceptos que son necesarios para crear software robusto. Existe una fase del desarrollo exclusiva, la fase de pruebas, centrada en este objetivo. No obstante, podemos aprender como implementar estos tratamientos y controles utilizando Java.

Las Excepciones

Las Excepciones son clases y como tales pueden ser definidas, instanciadas y lanzadas por el programador desde el código.

La clase padre para todos los errores es la clase Throwable, y ésta tiene dos hijos directos muy importantes: Error y Exception.

  • La clase Error se corresponde a los errores en tiempo de compilación: Son errores que evitan que el programa pueda ser ejecutado. Normalmente errores en sintáxis que impiden que el intérprete pueda comprender el código fuente, importaciones incorrectas de librerías, errores de memoria en la máquina virtual de Java, etc.
  • La clase Exception, por su parte, corresponde a aquellos errores que suceden en tiempo de ejecución: Valga la redundancia, son los errores que tienen lugar durante la ejecución del programa. En Java estos errores son eventos que se lanzan en tiempo de ejecución y nos obligan a salir del flujo habitual del programa. Son los errores que pueden ser capturados y controlados.

excepciones java
Dentro del grupo de subclases que heredan de Exception, tenemos otra muy importante de la que heredan otro gran número de excepciones, RuntimeException, que engloba todos aquellos errores que pueden ser prevenidos en nuestro código. Estos errores se conocen como unchecked exceptions.

Las excepciones checked son todas las demás que heredan de Exception y se deben a elementos ajenos al programa y de las que, previsiblemente, nos podremos recuperar. En Java es obligatorio la creación de bloques de captura de errores (que a continuación se verá como se especifican) o el reelanzado de las excepciones desde el método donde se produzcan cuando son de este tipo.

Captura de Excepciones

Cuando tenemos un bloque de código susceptible a tener errores que queremos controlar, o que pueda ejecutar excepciones checked que obliguen a su tratamiento, utilizaremos un nueva sentencia de flujo, la sentencia try-catch-finally. Ya se habló de ella en la pildorita de conceptos básicos de programación sobre sentencias, pero sólo vimos su sintáxis muy por encima pero como ya señalé, no podíamos entrar en mayor profundidad hasta ahora, que ya conocemos el concepto de excepción.

La sentencia try-catch-finally consta de tres bloques:

  • Try: en este bloque se incluirá el código a ejecutar del programa en la que queremos controlar los posibles errores que tengan lugar.
  • Catch: Indica la excepción que captura y el código que implementará su tratamiento.
  • Finally: Este bloque es opcional, y contiene código que se ejecutará siempre, se produzca una excepción o no. Este bloque tiene sentido para asegurarlos de realizar algunas tareas siempre, aunque una excepción haya parado a medias la ejecución del programa. Ejemplos típicos son el cierre de un fichero, de una conexión de BBDD, etc.

Lanzamiento de excepciones

Al igual que podemos capturar excepciones, podemos lanzarlas y relanzarlas a través de nuestros métodos.
Para que un método pueda pasar como mensaje una excepción al método que le invoca, en caso de producirse, se tendrá que indicar la palabra throws, seguida de los tipos de Excepciones que pueden ser lanzadas.

Por otra parte, podemos también lanzar nosotros una excepción controlada desde nuestro código. Esta es una buena forma, por ejemplo, de implementar errores funcionales en la aplicación que de otra forma no sería posible informar sobre ellos, y que en otros lenguajes se comunican de forma más burda, como es el caso de C, a través del retorno de la función de diferentes códigos de error. Para ello, utilizaremos la palabra reservada throw.

Creación de excepciones

Como hemos visto en el anterior ejemplo, podemos crear excepciones propias, por ejemplo para definir errores funcionales que no son en sí errores de programación, pero que dada la lógica de la aplicación impiden que se realice la funcionalidad completa.

Ejemplo

Para entender los conceptos descritos hasta ahora, vamos a ver un ejemplo muy básico con nuestro ejemplo de Profesores y Alumnos que englobará todos los conceptos vistos hasta el momento.

En primer lugar, vamos a crearnos una Excepción propia, UniversidadException, que se lanzará cuando encontremos comportamientos prohibidos desde la lógica de la aplicación.

package exception;

public class UniversidadException extends Exception {

	private static final long serialVersionUID = 1L;

	public UniversidadException(String msg) {
        super(msg);
    }
}

Como se puede observar no tiene mucha complicación. Para crear una excepción propia sólo tenemos que heredar de Exception (también podríamos heredar de Excepciones que hereden a su vez de Exception).

En nuestro caso solamente reimplementamos su constructor para que pase nuestro mensaje al constructor de Exception, pero podríamos, por ejemplo, crear campos de códigos de error y descriptivos cortos, que se tuvieran en cuenta en este constructor y a la hora de obtener sus mensajes, con un @Override al metodo getMessage.

Por otra parte, vamos a coger nuestra implementación de Alumno vista en pildoritas anteriores, y modificaremos un poco el método de getNotaMedia con una nueva condición funcional:

public double getNotaMedia() throws UniversidadException {
		double resultado = 0;
		
		if (asignaturas == null || asignaturas.size() == 0) {
			throw new UniversidadException("El alumno tiene que estar matriculado al menos en una asignatura");
		} else {
			for (Asignatura asig: asignaturas) 
				resultado += asig.getNota();
		}
		return (resultado != 0 ? resultado / asignaturas.size(): 0);
	}

Como se observa, hemos añadido una condición que comprueba la longitud de las asignaturas asociadas al Alumno, y en caso de que ésta sea 0, se lanzará un error con el descriptivo “El alumno tiene que estar matriculado al menos en una asignatura“.

Al añadir este lanzamiento de excepción el propio Eclipse obliga a, o bien añadir un método try-catch que la capture, o bien un throws en la cabecera para que el método envíe hacia el invocador dicha excepción. En este caso se ha añadido en la cabecera un método throws UniversidadException para que la excepción sea tratada por aquella clase que ha llamado al método getNotaMedia.

Por tanto, el invocador tendrá que verse obligado a introducir el bloque try-catch que captura la excepción y hace el tratamiento oportuno, en este caso mostrar por pantalla la traza de error:

		try {
			alumno.getNotaMedia();
		} catch (UniversidadException e) {
			e.printStackTrace();
		}

En caso de lanzar un alumno sin asignaturas, se mostrará por consola una traza como la siguiente:

exception.UniversidadException: El alumno no puede estar matriculado a 0 asignaturas
at model.persona.Alumno.getNotaMedia(Alumno.java:40)
at model.persona.TestException.main(TestException.java:7)

El método printStackTrace, heredado de Exception, muestra el message de la excepción y una traza de las clases y líneas donde ha tenido lugar ese error.

Como véis el tratamiento de excepciones en Java es muy sencillo pero al mismo tiempo muy potente. El uso de try-catchs en nuestro código convertirá nuestros desarrollos en aplicaciones estables y controladas. Por otra parte, tampoco se debe abusar de este mecanismo, ya que las excepciones, como su nombre indica, deben ser lanzadas sólo en casos excepcionales.

¿Y ahora qué?

Pues no lo tengo claro. Este era la última pildorita que tenía planificada, por lo que a partir de aquí todo es campo. Mi primera idea era hacer un ejemplo completo de aplicación en Java para asentar lo visto en las pildoritas, pero con el ejemplo de los Alumnos y Profesores ya ha surgido un proyecto completo con todos esos conceptos. Seguramente sea lo siguiente que haga, subirlo a github con algún añadido extra, como una interfaz básica, y explicar esos nuevos componentes.

Por otra parte, hay otro montón de conceptos y conocimientos fundamentales que no han sido vistos en esta serie. Es el caso de los eventos, por poner un ejemplo, que se han vislumbrado levemente en esta pildorita y que dan lugar a todo un paradigma nuevo, la programación reactiva. También sería muy interesante ver conceptos de programación concurrente y el uso de hilos, patrones de diseño, o cosas tan básicas como la lectura y escritura de ficheros, comunicaciones con bases de datos, uso de los frameworks más conocidos…

Como véis, hay mucha tela por cortar, la cuestión ahora es como organizar toda esa tela y cortarla de tal manera que nos quede un trajecito de picateclas la mar de mono. Mientras pienso en ello, agradecería cualquier consejo o idea, así que no os cortéis 🙂

Índice de Pildoritas

Share