En Java (o en cualquier otro idioma con excepciones marcadas), al crear su propia clase de excepción, ¿cómo decide si se debe marcar o no?
Mi instinto es decir que se solicitaría una excepción marcada en los casos en que la persona que llama podría recuperarse de una manera productiva, donde una excepción no verificada sería más para casos irrecuperables, pero estaría interesado en los pensamientos de otros.
java
exception
checked-exceptions
Matt Sheppard
fuente
fuente
Respuestas:
Las excepciones marcadas son geniales, siempre y cuando comprenda cuándo deben usarse. La API principal de Java no sigue estas reglas para SQLException (y a veces para IOException), por lo que son tan terribles.
Las excepciones marcadas deben usarse para errores predecibles , pero no evitables , de los que es razonable recuperarse .
Las excepciones no marcadas deben usarse para todo lo demás.
Desglosaré esto por ti, porque la mayoría de la gente no entiende lo que esto significa.
A menos que la excepción que esté lanzando cumpla con todas las condiciones anteriores, debe usar una excepción no verificada.
Reevaluar en todos los niveles : a veces, el método para detectar la excepción marcada no es el lugar adecuado para manejar el error. En ese caso, considere lo que es razonable para sus propias personas que llaman. Si la excepción es predecible, evitable y razonable para que se recuperen, entonces debe lanzar una excepción marcada usted mismo. De lo contrario, debe ajustar la excepción en una excepción no marcada. Si sigue esta regla, se encontrará convirtiendo excepciones marcadas en excepciones no verificadas y viceversa, dependiendo de la capa en la que se encuentre.
Para las excepciones marcadas y no marcadas, use el nivel de abstracción correcto . Por ejemplo, un repositorio de código con dos implementaciones diferentes (base de datos y sistema de archivos) debe evitar exponer detalles específicos de la implementación lanzando
SQLException
oIOException
. En cambio, debería envolver la excepción en una abstracción que abarque todas las implementaciones (por ejemploRepositoryException
).fuente
De un aprendiz de Java :
fuente
Hay un caso muy fuerte para lo contrario: nunca use excepciones marcadas. Soy reacio a tomar partido en el debate, pero parece haber un amplio consenso de que introducir excepciones controladas fue una decisión equivocada en retrospectiva. Por favor, no dispare al mensajero y consulte esos argumentos .
fuente
foo
se documenta como arrojarbarException
al leer más allá del final de un archivo, yfoo
llama a un método que arrojabarException
aunquefoo
no lo espera, el codifique qué llamadasfoo
pensarán que se alcanzó el final del archivo y no tendrán idea de que sucedió algo inesperado. Consideraría que esa situación es aquella en la que las excepciones verificadas deberían ser más útiles, pero también es el único caso en el que el compilador permitirá excepciones verificadas no controladas.En cualquier sistema lo suficientemente grande, con muchas capas, la excepción marcada es inútil ya que, de todos modos, necesita una estrategia de nivel arquitectónico para manejar cómo se manejará la excepción (use una barrera de falla)
Con excepciones marcadas, su estrategia de manejo de errores está microgestionada y es insoportable en cualquier sistema grande.
La mayoría de las veces no sabe si un error es "recuperable" porque no sabe en qué capa se encuentra la persona que llama de su API.
Digamos que creo una API StringToInt que convierte la representación de cadena de un entero en un Int. ¿Debo lanzar una excepción marcada si se llama a la API con la cadena "foo"? ¿Es recuperable? No lo sé porque en su capa la persona que llama de mi API StringToInt ya puede haber validado la entrada, y si se lanza esta excepción, es un error o una corrupción de datos y no es recuperable para esta capa.
En este caso, la persona que llama de la API no quiere detectar la excepción. Él solo quiere dejar que la excepción "brote". Si elegí una excepción marcada, esta persona que llama tendrá un montón de bloqueos inútiles solo para volver a lanzar artificialmente la excepción.
Lo que es recuperable depende la mayor parte del tiempo del llamante de la API, no del escritor de la API. Una API no debe usar excepciones marcadas, ya que solo las excepciones no marcadas permiten elegir capturar o ignorar una excepción.
fuente
Estás en lo correcto.
Se utilizan excepciones no verificadas para permitir que el sistema falle rápidamente, lo cual es bueno. Debe indicar claramente qué espera su método para que funcione correctamente. De esta manera, puede validar la entrada solo una vez.
Por ejemplo:
Solo para poner un ejemplo. El punto es que si el sistema falla rápidamente, entonces sabrás dónde y por qué falló. Obtendrás un stacktrace como:
Y sabrás lo que pasó. OtherClass en el método "delegateTheWork" (en la línea 4569) llamó a su clase con el valor "exit", incluso cuando no debería, etc.
De lo contrario, tendría que rociar validaciones en todo el código y eso es propenso a errores. Además, a veces es difícil hacer un seguimiento de lo que salió mal y puede esperar horas de depuración frustrante
Lo mismo sucede con NullPointerExceptions. Si tiene una clase de 700 líneas con unos 15 métodos, que usa 30 atributos y ninguno de ellos puede ser nulo, en lugar de validar en cada uno de esos métodos la nulabilidad, puede hacer que todos esos atributos sean de solo lectura y validarlos en el constructor o método de fábrica
Excepciones comprobadas Son útiles cuando el programador (usted o sus compañeros de trabajo) hicieron todo bien, validaron la entrada, realizaron pruebas y todo el código es perfecto, pero el código se conecta a un servicio web de terceros que puede estar inactivo (o un archivo que estaba usando fue eliminado por otro proceso externo, etc. El servicio web puede incluso validarse antes de intentar la conexión, pero durante la transferencia de datos algo salió mal.
En ese escenario, no hay nada que usted o sus compañeros de trabajo puedan hacer para ayudarlo. Pero aún debe hacer algo y no dejar que la aplicación simplemente muera y desaparezca a los ojos del usuario. Utiliza una excepción marcada para eso y maneja la excepción, ¿qué puede hacer cuando eso sucede ?, la mayoría de las veces, solo para intentar registrar el error, probablemente guarde su trabajo (el trabajo de la aplicación) y presente un mensaje al usuario . (El sitio blabla está inactivo, vuelva a intentarlo más tarde, etc.)
Si la excepción marcada se usa en exceso (agregando la "excepción de lanzamiento" en todas las firmas de métodos), entonces su código se volverá muy frágil, porque todos ignorarán esa excepción (porque es demasiado general) y la calidad del código será seriamente comprometidos.
Si usa en exceso la excepción no marcada, sucederá algo similar. Los usuarios de ese código no saben si algo puede salir mal y aparecerán muchos intentos {...} catch (Throwable t).
fuente
Aquí está mi 'regla de oro final'.
Yo suelo:
Compare con la respuesta anterior, esta es una razón clara (sobre la cual uno puede estar de acuerdo o en desacuerdo) para el uso de uno u otro (o ambos) tipo de excepciones.
Para ambas excepciones, crearé mi propia Excepción sin marcar y marcada para mi aplicación (una buena práctica, como se menciona aquí ), excepto por una excepción sin marcar muy común (como NullPointerException)
Entonces, por ejemplo, el objetivo de esta función particular a continuación es hacer (u obtener si ya existe) un objeto, lo que
significa:
=> excepción no marcada, y borrar el comentario de javadoc para esta función llamada)
(elección del codificador para poner eso en el LLAMADOR: el codificador no verificará el parámetro nulo pero el codificador LO DOCUMENTA)
(responsabilidad y elección del código de la persona que llama, elección que será de gran interés para la persona que llama
=> la excepción marcada porque cada persona que llama DEBE tomar una decisión si el objeto no se puede crear / encontrar, y eso la decisión debe hacerse cumplir en el momento de la compilación: no pueden usar esta función sin tener que lidiar con esta posibilidad, es decir, con esta excepción marcada ).
Ejemplo:
fuente
No se trata solo de la capacidad de recuperarse de la excepción. Lo más importante, en mi opinión, es si la persona que llama está interesada en detectar la excepción o no.
Si escribe una biblioteca para usar en otro lugar, o una capa de nivel inferior en su aplicación, pregúntese si la persona que llama está interesada en detectar (saber) su excepción. Si no lo está, use una excepción no marcada, para no cargarlo innecesariamente.
Esta es la filosofía utilizada por muchos marcos. Spring e hibernate, en particular, vienen a la mente: convierten la excepción comprobada conocida en una excepción no comprobada precisamente porque las excepciones comprobadas se usan en exceso en Java. Un ejemplo en el que puedo pensar es la JSONException de json.org, que es una excepción marcada y es en su mayoría molesta: debe estar desmarcada, pero el desarrollador simplemente no lo ha pensado.
Por cierto, la mayoría de las veces el interés de la persona que llama en la excepción está directamente relacionado con la capacidad de recuperarse de la excepción, pero ese no es siempre el caso.
fuente
Aquí hay una solución muy simple para su dilema marcado / no marcado.
Regla 1: piense en una excepción no verificada como una condición comprobable antes de que se ejecute el código. por ejemplo…
donde x es nulo ... ... el código posiblemente debería tener lo siguiente ...
Regla 2: piense en una excepción marcada como una condición no comprobable que puede ocurrir mientras se ejecuta el código.
... en el ejemplo anterior, es posible que la URL (google.com) no esté disponible debido a que el servidor DNS está inactivo. Incluso en el instante en que el servidor DNS estaba funcionando y resolvió el nombre 'google.com' a una dirección IP, si la conexión se realiza a google.com, en cualquier momento posterior, la red podría fallar. Simplemente no puede probar la red todo el tiempo antes de leer y escribir en las transmisiones.
Hay momentos en los que el código simplemente debe ejecutarse antes de que podamos saber si hay un problema. Al obligar a los desarrolladores a escribir su código de tal manera que los obligue a manejar estas situaciones a través de la Excepción Comprobada, tengo que inclinarme ante el creador de Java que inventó este concepto.
En general, casi todas las API en Java siguen las 2 reglas anteriores. Si intenta escribir en un archivo, el disco podría llenarse antes de completar la escritura. Es posible que otros procesos hayan causado que el disco se llene. Simplemente no hay forma de probar esta situación. Para aquellos que interactúan con el hardware en cualquier momento, el uso del hardware puede fallar, las Excepciones comprobadas parecen ser una solución elegante para este problema.
Hay un área gris en esto. En el caso de que se necesiten muchas pruebas (una declaración alucinante de if con muchos && y ||), la excepción lanzada será una excepción CheckedException simplemente porque es demasiado difícil hacerlo bien; simplemente no puede decir este problema Es un error de programación. Si hay menos de 10 pruebas (por ejemplo, 'if (x == null)'), el error del programador debe ser una excepción no verificada.
Las cosas se ponen interesantes cuando se trata de intérpretes de idiomas. De acuerdo con las reglas anteriores, ¿se debe considerar un error de sintaxis como una excepción marcada o no marcada? Yo diría que si la sintaxis del lenguaje se puede probar antes de que se ejecute, debería ser una excepción no verificada. Si no se puede probar el idioma, de forma similar a cómo se ejecuta el código de ensamblaje en una computadora personal, entonces el Error de sintaxis debería ser una excepción comprobada.
Las 2 reglas anteriores probablemente eliminarán el 90% de su preocupación sobre la cual elegir. Para resumir las reglas, siga este patrón ... 1) si el código que se ejecutará puede probarse antes de ejecutarse para que se ejecute correctamente y si se produce una excepción, es decir, un error del programador, la excepción debería ser una excepción no verificada (una subclase de RuntimeException ) 2) si el código que se va a ejecutar no se puede probar antes de que se ejecute para que se ejecute correctamente, la excepción debe ser una excepción comprobada (una subclase de excepción).
fuente
Puede llamarlo una excepción marcada o no marcada; sin embargo, el programador puede detectar ambos tipos de excepción, por lo que la mejor respuesta es: escriba todas sus excepciones como desmarcadas y documente. De esa forma, el desarrollador que usa su API puede elegir si quiere atrapar esa excepción y hacer algo. Las excepciones marcadas son una completa pérdida de tiempo para todos y hacen que su código sea una pesadilla impactante. Las pruebas unitarias adecuadas luego mostrarán cualquier excepción que pueda tener que atrapar y hacer algo.
fuente
Excepción marcada : si el cliente puede recuperarse de una excepción y desea continuar, use la excepción marcada.
Excepción no verificada: si un cliente no puede hacer nada después de la excepción, entonces genere una excepción no verificada.
Ejemplo: si se espera que realice una operación aritmética en un método A () y se base en la salida de A (), debe realizar otra operación. Si la salida es nula del método A () que no esperaba durante el tiempo de ejecución, se espera que arroje una excepción de puntero nulo, que es la excepción del tiempo de ejecución.
Consulte aquí
fuente
Estoy de acuerdo con la preferencia por las excepciones no marcadas como regla, especialmente al diseñar una API. La persona que llama siempre puede elegir capturar una excepción documentada y sin marcar. Simplemente no estás obligando innecesariamente a la persona que llama.
Encuentro excepciones comprobadas útiles en el nivel inferior, como detalle de implementación. A menudo parece un mejor mecanismo de flujo de control que tener que gestionar un error específico "código de retorno". A veces también puede ayudar a ver el impacto de una idea para un cambio de código de bajo nivel ... declarar una excepción comprobada en sentido descendente y ver quién necesitaría ajustar. Este último punto no se aplica si hay muchos genéricos: atrapar (Excepción e) o lanzar Excepción, que de todos modos no está muy bien pensada.
fuente
Aquí quiero compartir mi opinión que tengo después de muchos años de experiencia en desarrollo:
Excepción marcada. Esto es parte del caso de uso comercial o flujo de llamadas, es parte de la lógica de la aplicación que esperamos o no esperamos. Por ejemplo, conexión rechazada, condición no satisfecha, etc. Necesitamos manejarlo y mostrar el mensaje correspondiente al usuario con instrucciones de lo que sucedió y qué hacer a continuación (intente nuevamente más tarde, etc.). Por lo general, lo llamo excepción de procesamiento posterior o excepción de "usuario".
Excepción no verificada. Esto es parte de la excepción de programación, algún error en la programación del código de software (error, defecto) y refleja la forma en que los programadores deben usar la API según la documentación. Si un documento lib / framework externo dice que espera obtener datos en algún rango y no nulo, porque se lanzará NPE o IllegalArgumentException, el programador debe esperarlo y usar la API correctamente según la documentación. De lo contrario, se lanzará la excepción. Normalmente lo llamo excepción de preprocesamiento o excepción de "validación".
Por público objetivo. Ahora hablemos sobre el público objetivo o grupo de personas, las excepciones han sido diseñadas (según mi opinión):
Por fase de ciclo de vida de desarrollo de aplicaciones.
La razón por la cual los frameworks usualmente usan excepciones no verificadas (Spring, por ejemplo) es que el framework no puede determinar la lógica de negocios de su aplicación, esto depende de los desarrolladores y luego diseñar su propia lógica.
fuente
Tenemos que distinguir estos dos tipos de excepción en función de si es un error del programador o no.
FileNotFoundException es un buen ejemplo para comprender diferencias sutiles. FileNotFoundException se lanza en caso de que no se encuentre el archivo. Hay dos razones para esta excepción. Si el desarrollador define la ruta del archivo o la toma del usuario final a través de la GUI, debería ser una excepción no verificada. Si alguien más elimina el archivo, debería ser una excepción marcada.
La excepción marcada se puede manejar de dos maneras. Estos están usando try-catch o propagan la excepción. En caso de propagación de excepción, todos los métodos en la pila de llamadas estarán estrechamente acoplados debido al manejo de excepciones. Por eso, tenemos que usar la Excepción Comprobada con cuidado.
En caso de que desarrolle un sistema empresarial en capas, debe elegir una excepción mayormente no marcada para lanzar, pero no olvide usar la excepción marcada para el caso de que no pueda hacer nada.
fuente
Las excepciones marcadas son útiles para casos recuperables en los que desea proporcionar información a la persona que llama (es decir, permisos insuficientes, archivo no encontrado, etc.).
Las excepciones no verificadas se usan raramente, si es que lo hacen, para informar al usuario o programador de errores graves o condiciones inesperadas durante el tiempo de ejecución. No los arroje si está escribiendo código o bibliotecas que serán utilizados por otros, ya que es posible que no esperen que su software arroje excepciones no verificadas, ya que el compilador no obliga a que sean capturados o declarados.
fuente
Siempre que sea menos probable que se espere una excepción, y podemos proceder incluso después de atrapar eso, y no podemos hacer nada para evitar esa excepción, entonces podemos usar la excepción marcada.
Siempre que queramos hacer algo significativo cuando ocurra una excepción en particular y cuando esa excepción sea esperada pero no segura, entonces podemos usar la excepción marcada.
Siempre que la excepción navegue en diferentes capas, no necesitamos atraparla en cada capa, en ese caso, podemos usar la excepción de tiempo de ejecución o la excepción de ajuste como excepción no verificada.
La excepción de tiempo de ejecución se usa cuando es más probable que ocurra una excepción, no hay forma de ir más allá y nada puede ser recuperable. Entonces, en este caso, podemos tomar precauciones con respecto a esa excepción. EJ: NUllPointerException, ArrayOutofBoundsException. Es más probable que sucedan. En este escenario, podemos tomar precauciones mientras codificamos para evitar dicha excepción. De lo contrario, tendremos que escribir try catch blocks en todas partes.
Se pueden hacer excepciones más generales Sin marcar, se verifican las menos generales.
fuente
Creo que podemos pensar en excepciones a partir de varias preguntas:
¿Por qué sucede la excepción? ¿Qué podemos hacer cuando sucede?
por error, un error. como se llama un método de objeto nulo.
Este tipo de excepción se debe solucionar durante la prueba. De lo contrario, se interrumpe la producción y se obtiene un error muy alto que debe corregirse de inmediato. No es necesario verificar este tipo de excepciones.
por entrada externa, no puede controlar ni confiar en la salida del servicio externo.
Aquí, es posible que deba verificar si el nombre es nulo si desea continuar cuando sea nulo, de lo contrario, puede dejarlo solo y se detendrá aquí y le dará a la persona que llama la excepción de tiempo de ejecución. No es necesario verificar este tipo de excepciones.
por excepción de tiempo de ejecución externo, no puede controlar o confiar en el servicio externo.
Aquí, es posible que deba detectar todas las excepciones de ExternalService si desea continuar cuando suceda; de lo contrario, puede dejarlo solo y se detendrá aquí y le dará al llamante la excepción de tiempo de ejecución.
mediante la excepción marcada de externo, no puede controlar o confiar en el servicio externo.
Aquí, es posible que deba detectar todas las excepciones de ExternalService si desea continuar cuando suceda; de lo contrario, puede dejarlo solo y se detendrá aquí y le dará al llamante la excepción de tiempo de ejecución.
En este caso, ¿necesitamos saber qué tipo de excepción ocurrió en ExternalService? Depende:
Si puede manejar algunos tipos de excepciones, debe atraparlas y procesarlas. Para otros, burbujearlos.
Si necesita iniciar sesión o responder al usuario la ejecución específica, puede atraparlos. Para otros, burbujearlos.
fuente
Creo que al declarar la excepción de la aplicación debería ser la excepción no verificada, es decir, la subclase de RuntimeException. La razón es que no saturará el código de la aplicación con la declaración try-catch y throws en el método. Si su aplicación está utilizando Java Api que arroja excepciones comprobadas que de todos modos deben ser manejadas. Para otros casos, la aplicación puede lanzar una excepción no verificada. Si el llamador de la aplicación aún necesita manejar una excepción no verificada, puede hacerlo.
fuente
La regla que uso es: ¡nunca use excepciones no marcadas! (o cuando no ves ninguna forma de evitarlo)
Desde el punto de vista del desarrollador que usa su biblioteca o el usuario final que usa su biblioteca / aplicación, realmente apesta ser confrontado con una aplicación que se bloquea debido a una excepción no obtenida. Y contar con una captura general tampoco es bueno.
De esta manera, el usuario final aún puede recibir un mensaje de error, en lugar de que la aplicación desaparezca por completo.
fuente