2009-08-03 14 views
16

¿Cuál es la mejor práctica a seguir cuando necesita lanzar una excepción que no estaba definida en una interfaz que está implementando?Lanzar una excepción no definida en la interfaz

Aquí se muestra un ejemplo:

public interface Reader 
{ 
    public abstract void read() throws IOException; 
} 

public class CarrotReader implements Reader 
{ 
    public void read() throws IOException {} 
} 

public class CupcakeReader implements Reader 
{ 
    public void read() throws IOException, CupcakeException {} 
} 

En este caso, usted tiene una excepción específica que se produce cuando la lectura de pastelitos, por lo que desea lanzar una excepción en relación con esto. Sin embargo, Reader no define este tipo de excepción en su interfaz, entonces, ¿qué hace usted? Por otra parte, no tiene sentido para añadir CupcakeException a la tiros cláusula en la interfaz del lector , porque este tipo de excepción es específico para CupcakeReader. Una forma de evitar esto es tener Lector definir leer de manera que arroje algún tipo parental, como Excepción, pero luego pierde el contexto de la excepción. ¿Qué deberías hacer en esta situación? ¡Gracias!


Otra situación interesante que se ha planteado implica una interfaz sobre la que no tiene control. En este caso, ¿cuál es la mejor manera de indicar que ha ocurrido un problema?

Para fines ilustrativos, aquí es otro ejemplo:

public interface Reader 
{ 
    public abstract void read(); 
} 

public class CupcakeReader implements Reader 
{ 
    public void read() throws CupcakeException {} 
} 

En este caso, no se puede cambiar lector, pero desea indicar que ha ocurrido un problema en CupcakeReader 's leer método.

+0

Eso es aún peor de lo que pensaba. De esta forma, ni siquiera puede usar el encadenamiento para eludir el sistema. Creo que debes recurrir a (¡ouch!) 'RuntimeExceptions'. –

+0

Recurrí a System.err.println y a exception.printStackTrace() ;. ¿Es apropiado, o debería propagar el error con un RuntimeExecption como dijiste? – Scott

+3

Creo que registrar + tragar no es apropiado el 99,99% del tiempo. Es un patrón común (¿contra -?) Entre desarrolladores Java debido al problema presentado aquí. En mi "escuela de pensamiento", solo hay tres formas válidas de lidiar con las excepciones: volver a tirar, manejar (es decir, solucionar el problema) o dejar que le suba a alguien que pueda manejarlo. Nunca tragues. Si algo salió mal, habrá consecuencias. –

Respuesta

12

En su lugar, debe crear una excepción del tipo esperado.

... catch(CupcakeException e) { 
    throw new IOException("The sky is falling", e); 
} 
+1

+1 para mencionar el encadenamiento de excepciones. –

+0

Esto es lo que estoy haciendo en este momento. – Scott

+0

Esta es probablemente la mejor solución si no puede cambiar la interfaz. Todavía puede acceder a toda la información que pueda necesitar, y aún sigue el contrato de la interfaz. –

0

Quizás podría hacer una clase abstracta ReaderSpecificException, ponerla en la interfaz y subclase CupcakeException de esta clase abstracta.

+1

No creo que sea una buena práctica crear una excepción como clase abstracta. De forma predeterminada, una excepción sin atributos puede dar una idea del problema (simplemente eligiendo el nombre correcto). ¿Por qué querrías definirlo como una clase abstracta? – Manglu

9

Use algo llamado ReaderException que servirá como la interfaz raíz de su jerarquía de excepciones. ReaderException también proporciona un enlace a otras excepciones que se lanzan debido a excepciones de nivel inferior.

+0

¿No es esto lo mismo que agregar Excepción a la cláusula throws? Me doy cuenta de que al definir tu propia clase para padres que es algo más específica, conservas más contexto, pero en algún nivel sigues perdiendo información, ¿no? – Scott

+0

Eso es lo que suelo hacer (+1). ¿Pero qué harías si no pudieras cambiar la interfaz (el código no es tuyo, el código ya ha sido enviado, etc.)? –

+0

@Scott: no pierdes nada. Si lanza una 'CupcakeException' que extiende' ReaderException', puede atraparla como 'ReaderException' o como' CupcakeException', dependiendo de cuán específico desee ser. –

0

Si crea una excepción abstracta superior que funciona como una clase base para CupCakeException no asociar la interfaz del lector a una aplicación específica como la que estaría haciendo si se ha añadido la CupCakeException a la interfaz del lector. Si no dejas que una Excepción herede de otra, hay una constructor en la clase de excepción que toma un throwable como segundo argumento como Thorbjørn Ravn Andersen mostrado en su ejemplo de código corto. Le permite generar una excepción más abstracta y cada parte de su código que necesita saber más que solo "hay un error" puede buscar la causa de la excepción más alta.

1

Simplemente no use excepciones marcadas.

El ejemplo que mostró es uno de los motivos por los que las excepciones comprobadas son malas.

La razón principal es que el usuario de su lector de cupcake tendrá que manejar su excepción, independientemente de si le interesa o no.

Así que en lugar de:

Value value = reader.read(); 

Usted lo está obligando a hacer esto:

Value value = null; 
try { 
    value = reader.read(); 
} catch (Exception e) { 
    // now what?? 
} 

value.doSomething(); // potential NPE here 

Piense cuál es mejor, más fácil de leer y menos propenso a errores y simplemente deja de usar excepciones comprobadas.

EDIT:

Estoy sorprendido con la calificación negativa. ¿Hay personas que todavía piensan que las excepciones marcadas son excelentes? Si es así aquí hay algunas referencias por las que no debe utilizar excepciones comprobadas:

excepciones
  • Ningún marco moderna utiliza controladas (primavera, EJB3 etc)
  • artículo con ejemplos de código here
  • Stackoverflow topic
  • eficaz Java (secciones 58 y 59) - here
+4

Creo que estás publicando un ejemplo deliberadamente engañoso. Escribiría lo anterior para declarar y usar la variable dentro del bloque try {}. No hay ninguna razón por la que deba escribir código como el anterior (independientemente de si las excepciones están marcadas o no) –

+5

El problema con las excepciones marcadas no es el que se presenta aquí. Como Brian señaló, este código es simplemente incorrecto. Un problema con las excepciones comprobadas es que pierde cualquier beneficio de hacer comprobaciones preventivas (cuando puede) antes de llamar al código que arroja excepciones. Incluso si realiza controles preventivos, se verá obligado a utilizar un bloque try-catch. Otro es el hecho de que si tiene algo N llamadas de método profundiza que lanza, y solo llama 0 puede manejarlo, cada llamada de 0 a N tiene que incluir tiradas en el contrato (o trampa con 'RuntimeException's –

+0

Lea esto para más información sobre el "por qué no" de las excepciones marcadas: http://www.artima.com/intv/handcuffs.html –

2

La excepción forma parte de la interfaz. Defina un padre genérico para todas sus excepciones en la interfaz si puede redefinir la interfaz.

También puede hacer que CupcakeException sea un elemento secundario de IOException.

+0

Ten un "voto positivo" para el "también". Pero soy parcial, porque eso es lo que probablemente habría hecho (subclase de la excepción declarada, si es necesario). Por supuesto, tener una subclase de (por ejemplo,) IOException supone que la persona que llama necesita preocuparse de que ocurra algo más específico, así como implicar que el problema realmente es algún tipo de problema relacionado con E/S. – Roboprog

Cuestiones relacionadas