¿Qué significa "error: excepción no reportada <XXX>; debe detectarse o declararse lanzada" y cómo lo soluciono?

Resuelto Stephen C asked hace 2 años • 0 respuestas

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.
Stephen C avatar Jun 25 '22 13:06 Stephen C
Aceptado

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 ... catchdeclaración, o
    • declarando que el método o constructor adjunto throwses 1 .

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.Throwableclase. Las excepciones se dividen en dos categorías:

  • Las excepciones marcadas son Throwable, y Exceptionsus subclases, además de RuntimeExceptiony sus subclases.
  • Las excepciones no marcadas son todas las demás excepciones; es decir, Errory sus subclases, RuntimeExceptiony 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:

  1. Puede detectar la excepción en una try ... catchdeclaració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) IOExceptionen el cuerpo del archivo try. Luego escribimos una catchcláusula para detectar la excepción. (Podríamos capturar una superclase de IOException... pero en este caso sería así Exceptiony capturarlo Exceptiones una mala idea).

  2. Puede declarar que el método o constructor adjunto throwses la excepción

    public 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()throws IOException. Eso significa que cualquier código que llame al doThings()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:

  1. 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.
  2. Basado en 1, lea su código y decida la forma correcta de lidiar con la posible excepción.
  3. 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 tha 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 IllegalAccessExceptionel 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(o Throwable) como atajo para capturar una lista de excepciones. Si hace eso, es responsable de detectar cosas que no espera (como un archivo no marcado NullPointerException ) 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 staticlos inicializadores. Por ejemplo:

private static final FileInputStream input = new FileInputStream("foo.txt");

Se FileInputStreamdeclara 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.... catchY 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 staticbloque; 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á catchdetectando 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 deletearroje una excepción...)

Caso límite: dos excepciones llamadas Xyzzy

He visto casos en los que un método tiene una throws Xyzzycláusula y el compilador todavía se queja de que Xyzzydebe "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
Stephen C avatar Jun 25 '2022 06:06 Stephen C