¿Qué significa "error: excepción no reportada <XXX>; debe detectarse o declararse lanzada" y cómo lo soluciono?
Los nuevos programadores de Java frecuentemente encuentran errores redactados como este:
"error: unreported exception <XXX>; must be caught or declared to be thrown"
donde XXX es el nombre de alguna clase de excepción.
Por favor explique:
- Lo que dice el mensaje de error de compilación,
- los conceptos de Java detrás de este error, y
- Como arreglarlo.
Lo primero es lo primero. Este es un error de compilación, no una excepción. Deberías verlo en tiempo de compilación.
Si lo ve en un mensaje de excepción de tiempo de ejecución, probablemente se deba a que está ejecutando algún código con errores de compilación. Regrese y corrija los errores de compilación. Luego busque y establezca la configuración en su IDE que le impida generar archivos ".class" para el código fuente con errores de compilación. (Ahórrese el dolor futuro).
La respuesta corta a la pregunta es:
El mensaje de error dice que la declaración con este error genera (o propaga) una excepción marcada y que la excepción (la
XXX
) no se está tratando adecuadamente.La solución es abordar la excepción mediante:
- atraparlo y manejarlo con una
try ... catch
declaración, o - declarando que el método o constructor adjunto
throws
es 1 .
- atraparlo y manejarlo con una
1 - Hay algunos casos extremos en los que no puedes hacer eso. ¡Lee el resto de la respuesta!
Excepciones marcadas versus no marcadas
En Java, las excepciones están representadas por clases que descienden de la java.lang.Throwable
clase. Las excepciones se dividen en dos categorías:
- Las excepciones marcadas son
Throwable
, yException
sus subclases, además deRuntimeException
y sus subclases. - Las excepciones no marcadas son todas las demás excepciones; es decir,
Error
y sus subclases,RuntimeException
y sus subclases.
(En lo anterior, las "subclases" incluyen subclases directas e indirectas).
La distinción entre excepciones marcadas y no marcadas es que las excepciones marcadas deben "tratarse" dentro del método o constructor adjunto en el que ocurren, pero no es necesario tratar las excepciones no marcadas.
(P: ¿Cómo saber si una excepción está marcada o no? R: Busque el javadoc para la clase de la excepción y observe sus clases principales).
¿Cómo se maneja una excepción (marcada)?
Desde la perspectiva del lenguaje Java, hay dos formas de lidiar con una excepción que "satisfará" al compilador:
Puede detectar la excepción en una
try ... catch
declaración. Por ejemplo:public void doThings() { try { // do some things if (someFlag) { throw new IOException("cannot read something"); } // do more things } catch (IOException ex) { // deal with it <<<=== HERE } }
En lo anterior, colocamos la declaración que arroja (marcado)
IOException
en el cuerpo del archivotry
. Luego escribimos unacatch
cláusula para detectar la excepción. (Podríamos capturar una superclase deIOException
... pero en este caso sería asíException
y capturarloException
es una mala idea).Puede declarar que el método o constructor adjunto
throws
es la excepciónpublic void doThings() throws IOException { // do some things if (someFlag) { throw new IOException("cannot read something"); } // do more things }
En lo anterior hemos declarado que
doThings()
throwsIOException
. Eso significa que cualquier código que llame aldoThings()
método tiene que lidiar con la excepción. En resumen, estamos pasando el problema de lidiar con la excepción a la persona que llama.
¿Cuál de estas cosas es lo correcto?
Depende del contexto. Sin embargo, un principio general es que debe tratar las excepciones en un nivel del código en el que pueda hacerlo de forma adecuada. Y eso, a su vez, depende de lo que hará el código de manejo de excepciones (en HERE
). ¿Podrá recuperarse? ¿Puede abandonar la solicitud actual? ¿Debería detener la aplicación?
Resolviendo el problema
Recordar. El error de compilación significa que:
- su código ha arrojado una excepción marcada, o ha llamado a algún método o constructor que arroja la excepción marcada, y
- no se ha ocupado de la excepción captándola o declarándola como lo requiere el lenguaje Java.
Su proceso de solución debe ser:
- Comprenda qué significa la excepción y por qué se podría generar. Lea el javadoc para conocer la excepción y el método que genera la excepción.
- Basado en 1, lea su código y decida la forma correcta de lidiar con la posible excepción.
- Basado en 2, realice los cambios relevantes en su código.
Ejemplo: lanzar y atrapar con el mismo método
Considere el siguiente ejemplo de esta sesión de preguntas y respuestas.
public class Main {
static void t() throws IllegalAccessException {
try {
throw new IllegalAccessException("demo");
} catch (IllegalAccessException e){
System.out.println(e);
}
}
public static void main(String[] args){
t();
System.out.println("hello");
}
}
Si ha seguido lo que hemos dicho hasta ahora, se dará cuenta de que t()
aparecerá el error de compilación de "excepción no informada". En este caso el error es que t
ha sido declarado como throws IllegalAccessException
. De hecho, la excepción no se propaga porque ha sido capturada dentro del método que la lanzó.
La solución en este ejemplo será eliminar el archivo throws IllegalAccessException
.
La mini lección aquí es que throws IllegalAccessException
el método dice que la persona que llama debe esperar que la excepción se propague. En realidad, no significa que se propagará. Y la otra cara es que si no espera que la excepción se propague (por ejemplo, porque no se lanzó o porque fue detectada y no se volvió a lanzar), entonces la firma del método no debería decir que se lanzó.
Mala práctica con excepciones.
Hay un par de cosas que debes evitar hacer:
No capture
Exception
(oThrowable
) como atajo para capturar una lista de excepciones. Si hace eso, es responsable de detectar cosas que no espera (como un archivo no marcadoNullPointerException
) y luego intentar recuperarlas cuando no debería hacerlo.No declare un método como
throws Exception
. Eso obliga a la persona llamada a lidiar con (potencialmente) cualquier excepción marcada... lo cual es una pesadilla.No aplastes las excepciones. Por ejemplo
try { ... } catch (NullPointerException ex) { // It never happens ... ignoring this }
Si elimina las excepciones, es probable que los errores de tiempo de ejecución que las desencadenaron sean mucho más difíciles de diagnosticar. Estás destruyendo la evidencia.
Nota: el simple hecho de creer que la excepción nunca ocurre (según el comentario) no necesariamente lo convierte en un hecho.
Caso extremo: inicializadores estáticos
Hay algunas situaciones en las que lidiar con excepciones marcadas es un problema. Un caso particular son las excepciones marcadas en static
los inicializadores. Por ejemplo:
private static final FileInputStream input = new FileInputStream("foo.txt");
Se FileInputStream
declara como throws FileNotFoundException
... que es una excepción marcada. Pero dado que lo anterior es una declaración de campo, la sintaxis del lenguaje Java no nos permitirá colocar la declaración dentro de un try
.... catch
Y no existe un método o constructor apropiado (adjunto)... porque este código se ejecuta cuando se inicializa la clase .
Una solución es utilizar un static
bloque; Por ejemplo:
private static final FileInputStream input;
static {
FileInputStream temp = null;
try {
temp = new FileInputStream("foo.txt");
} catch (FileNotFoundException ex) {
// log the error rather than squashing it
}
input = temp; // Note that we need a single point of assignment to 'input'
}
(Hay mejores formas de manejar el escenario anterior en código práctico, pero ese no es el objetivo de este ejemplo).
Caso límite: bloques estáticos
Como se señaló anteriormente, puede detectar excepciones en bloques estáticos. Pero lo que no mencionamos es que debes detectar las excepciones marcadas dentro del bloque. No existe un contexto adjunto para un bloque estático donde se puedan detectar las excepciones marcadas.
Caso límite: lambdas
Una expresión lambda (normalmente) no debería generar una excepción no comprobada. Esta no es una restricción para lambdas per se . Más bien, es una consecuencia de la interfaz de función que se utiliza para el argumento donde se proporciona el argumento. A menos que la función declare una excepción marcada, lambda no puede generar una. Por ejemplo:
List<Path> paths = ...
try {
paths.forEach(p -> Files.delete(p));
} catch (IOException ex) {
// log it ...
}
Aunque parezca que hemos detectado IOException
, el compilador se quejará de que:
- hay una excepción no detectada en la lambda, Y
- ¡ Está
catch
detectando una excepción que nunca se lanza!
De hecho, la excepción debe quedar atrapada en la propia lambda:
List<Path> paths = ...
paths.forEach(p -> {
try {
Files.delete(p);
} catch (IOException ex) {
// log it ...
}
}
);
(El lector astuto notará que las dos versiones se comportan de manera diferente en el caso de que delete
arroje una excepción...)
Caso límite: dos excepciones llamadas Xyzzy
He visto casos en los que un método tiene una throws Xyzzy
cláusula y el compilador todavía se queja de que Xyzzy
debe "captarse o declararse arrojado". Por ejemplo: excepción no reportada... debe detectarse o declararse lanzada; a pesar de la palabra clave *lanzada* .
Si se encuentra en esta situación, observe atentamente los nombres completos de las excepciones. Si los nombres completos son diferentes, entonces las excepciones son diferentes. (Cree lo que dice el compilador e intenta entenderlo).
Más información
El tutorial de Oracle Java:
- El requisito de capturar o especificar ... también cubre excepciones marcadas y no marcadas.
- Captura y manejo de excepciones
- Especificación de las excepciones lanzadas por un método