Verificación de excepciones con estructuras condicionales

Verificación de excepciones con estructuras condicionales

Los setters nos permiten modificar de manera ordenada los atributos de los objetos y mantener el principio de encapsulamiento, por ello deben tener tres funciones:

  1. Verificar que el valor que se pretende asignar al atributo sea correcto.
  2. Asignar el valor al atributo, si es un valor válido.
  3. Verificar si no se altera el estado de otros atributos del objeto.

Al centrarnos en el primer punto, se proponen tres tratamientos para el manejo de la información, sobre todo en los setters:

  1. Ignorar el valor erróneo: ya sea porque puede no ser relevante. Por ejemplo: asignar un radio negativo a un círculo pudiera ser un error conceptual, sin embargo, podría en el contexto de un problema aceptarse.
  2. Intentar corregir el error: en el caso anterior, podría darse la asignación de un valor arbitrario cuando se introduzca un valor incorrecto o por ejemplo asignar el valor absoluto del valor que se envía para modificar el atributo.
  3. Generar una excepción y no dejar modificar el atributo: si no hay forma de corregir el error de asignación, en el contexto del problema, debe generarse una excepción, no asignar el valor erróneo y regresar el “problema” al programa principal o al método que intentó hacer una asignación errónea.

Ejemplo

Para la siguiente clase, intente implementar un método setter para el radio, considerando que conceptualmente los círculos no tienen radios negativos:

public class Circulo {

// Atributos: -----------------------------------------------------------

private double radio;

private double area;

// Métodos: -------------------------------------------------------------

public Circulo() {

this.setRadio(1);

}

private void calcularArea() {

this.area = Math.PI * Math.pow(this.radio,2);

}

public double getRadio() {

return this.radio;

}

public double getArea() {

return this.area;

}

public void setRadio( double value ) {

// AQUÍ DEBERIA VERIFICAR QUE value ES CORRECTO PARA EL ATRIBUTO

this.radio = value; // Modificar el atributo

this.calcularArea(); // Modificar el atributo

}

}

Ignorar

Al ignorar, no se hace verificación previa antes de asignar el atributo:

public void setRadio( double value ) {

this.radio = value; // Modificar el atributo

this.calcularArea(); // Modificar el atributo

}

Corregir

En el intento de corregir, se verifica primero que el valor sea correcto:

Verificación con if:

public void setRadio( double value ) {

if(value<0)

value = -value;

this.radio = value;

this.calcularArea();

}

Verificación con if-else:

public void setRadio( double value ) {

if(value<0)

this.radio = -value;

else

this.radio = value;

this.calcularArea();

}

Verificación con operador ternario:

public void setRadio( double value ) {

this.radio = (value<0) ? -value : value;

this.calcularArea();

}

Tratamiento formal de excepciones

Los dos primeros manejos se han visto previamente en este material, en este punto se revisará un manejo simple de excepciones.

public void setRadio( double value ) {

if(value<0)

this.excepcionArgumentoInvalido("El valor del radio debe ser positivo");

this.radio = value;

this.calcularArea();

}

Observa como se utiliza una condicional para determinar si value tiene un dato correcto y si no se declara un método para lanzar la excepción, así quedaría:

private double excepcionArgumentoInvalido ( String mensaje ) {

throw new IllegalArgumentException( mensaje );

}

Si en el programa principal quieres modificar el valor del radio con un argumento negativo el programa se detendrá por una excepción (Exception). En el ejemplo se instancia una excepción de tipo IllegalArgumentException, y se detiene la ejecución del método como si se hubiera escrito la palabra return y se ignoran el resto de las sentencias.

Al regresar el código a quien invocó el error por intentar modificar un atributo con un valor erróneo que no pudo determinarse como corregirlo, se detendrá la ejecución del programa a meno que se haga un manejo de la excepción.

El mecanismo es algo complejo pero a grandes rasgos se explica como se maneja: en el programa principal se codificará siguiendo este diagrama sintáctico.

Diagrama de flujo

El programa “asume” e intenta (try) ejecutar un bloque de código, en la mayoría de los casos no sucedera nada, pero dejo un “salvavidas” que correrá en auxilio si algo sale mal y atrapará (catch) la excepción lanzada (throw) por el método que no permitió la ejecución de algún código e intentará salvar el programa o por lo menos, salir de una forma “elegante”.

Circulo unCirculo = new Circulo();

try { // intenta modificar el radio:

unCirculo.setRadio( 5 ); // esta línea es correcta y continua sin pena ni gloria

unCirculo.setRadio( -10 ); // este es un valor incorrecto y genera una excepción

unCirculo.setRadio( 5 ); // esta línea es correcta pero se ignora, se interrumpe a partir de la línea que generó el erros

}

catch(Exception e ) { // Un salvavidas para cualquier excepción aun que lo mas correcto es poner

// lo pertinente, por ejemplo IllegalArgumentException

System.out.println( e.getMessage() ); // Escribe un mensaje de error y no termina el programa

// Aquí intenta recuperar al programa del desastre

}

Ejemplo, utilizando try-catch implementa un método que lea un número double desde un Scanner, si se introdujo un resultado erróneo intenta nuevamente hasta que sea correcto (la estructura do-while puedes revisarla en Estructuras de control iterativas):

private static double recuperarDouble() {

double tmp;

Scanner teclado = new Scanner(System.in);

do {

try {

tmp = new Double(teclado.nextLine()); // desde Scanner e intenta crear Double

}

catch ( Exception e ){ // si no es un doble, no falla el programa y tmp $\gets$ NoEsNumero

System.out.println( "El valor debe tener un formato de número." );

System.out.println( "Introduce de nuevo el número. " );

tmp = Double.NaN;

}

} while( Double.isNaN(tmp) );

return tmp;

}

Ejercicios

Suponiendo que en cada clase se cuenta con un método excepcionArgumentoInvalido que puede ser invocado en caso de que no se proporcione un dato correcto a un setter.

private double excepcionArgumentoInvalido ( String mensaje ) {

throw new IllegalArgumentException( mensaje );

}

Ejemplo

Implementa un método para asignar la estatura de una persona que verifique las siguientes excepciones:

  • El valor debe ser siempre positivo.
  • La estatura debe proporcionarse en metros.
  • La estatura debe estar entre 1.20 y 2.20 m.
  • Si se introduce una estatura en centímetros se corrige.
  • Si no es el rango adecuado debe generar formalmente una excepción y terminar el programa.

public void setEstatura(double value) {

value = Math.abs(value); // Corrige a positivo

if( (120 <= value) && (value <= 220) ) // Corrige de centímetros a metros en rango válido

value = value / 100;

if( (value>=1.20) && (value<=2.20) ) // Verifica rango válido en metros

this.estatura = value; // asigna si es correcto

else // no se pudo corregir

excepcionArgumentoInvalido( "La estatura debe estar entre 1.20 y 2.20 metros" );

this.calcularIMC(); // Corrige los atributos modificados como consecuencia

}

Ejercicio 1

Implementa un método para asignar el parámetro “a” cuadrático de una ecuación de segundo grado:

  • El valor debe ser distinto de cero.
  • Si no es el rango adecuado debe generar formalmente una excepción y terminar el programa.

public void setA(double value) {

if( value != 0 ) // Si el valor es válido

this.a = value ; // asigna si es correcto

else // no se pudo corregir

excepcionArgumentoInvalido( "El parámetro cuadrático (a) no puede valer cero" );

this.calcularRaices(); // Corrige los atributos modificados como consecuencia

}

Ejercicio 2

Implementa un método para asignar N a un objeto de TablaDeMultiplicar, considerando las siguientes excepciones:

  • El valor debe estar entre 1 y 10.
  • Si no es el rango adecuado debe generar formalmente una excepción y terminar el programa.

public void setN( int value ) {

if( value>=1 && value<=10 )

this.n = value; // asigna si es correcto

else

excepcionArgumentoInvalido( "Solo se calculan tablas de números entre 1 y 10" );

this.obtenerTabla(); // Corrige los atributos modificados como consecuencia

}