Excepción: ¿Heredar de Throwable o de Exception?
Ahora que estaba escribiendo una excepcion heredé como de costumbre desde Exception
. Al parecer no tengo broncas con el manejo de escepciones (al implementarlas) pero mi duda es acerca de ¿Cuál es la mejor practica para crear una excepción heredar de Exception
o directamente de Throwable
?.
Tengo por entendido que Throwable
tiene definido el comportamiento de un error generado en tiempo de ejecución, osea como el mesero que te trae la comida mientras que Exception
es el Hostess que te recibe en el restaurant y solo te lleva con tu mesero porque Exception
no tiene mas que los constructores definidos por Throwable
. Entonces, ¿de cuál es mejor heredar?
- Inicie sesión o regístrese para enviar comentarios
Error o Excepcion
Ya veo, la razón por la cuál se hereda desde Exception para indicar una excepción (válgame la redundancia) es porque
Throwable
tiene como subtipos:Error
yException
.Complemento con este post para aclarar las diferencias.
http://stackoverflow.com/questions/912334/differences-betweeen-exception...
RuntimeException
Lo mejor sería que heredes de RuntimeException. Así no obligas a nadie a tener que cachar tu excepción o declararla como que se arroja.
Originalmente los tres tipos
- Originalmente
Los tres tipos de excepciones se concibieron para manejarse asi:
Se hereda de Exception cuando no se puede hacer nada (con codigo) para evitar la excepcion.
Por ejemplo revisas que el archivo al que vas a escribir exista, pero cuando empiezas a escribir alguien (otro proceso) lo borra, no puedes hacer nada para evitar que lo borren, pero puedes hacer algo como alternativa y evitar que el programa se muera ( por ejemplo abrir un archivo temporal o regresar un mensaje de error pero seguir funcionando) La excepcion que se lanza en este ejemplo ser'ia IOException ( o alguna subclase ) y la alternativa (el manejo) es lo que se pone en el catch.
Se hereda de RunTimeException cuando se puede hacer algo para evitarlo (con codigo)
Por ejemplo intentas acceder a un indice negativo de un arreglo, se tira ArrayIntexOutOfBounds, o cuando el objeto al que quieres llamar es nulo (NullPointerException). Se podria hacer algo (con codigo) para evitarlo, como revisar los limites o mantener las referencias no nulas dentro de tu programa.
El tercer tipo de exception Error, es cuando hay algo fatal en el entorno del programa y no se puede hacer nada al respecto, ejemplo se acabo la memoria, no tiene caso intentar mandar un mensaje de error (ya no hay memoria para decir que ya no hay memoria )
- En la practica
Con los anios se ha demostrado que no tiene mucho sentido usar el segundo tipo (
extends Exception
), porque en la mayoria de los casos el codigo que manda la excepcion no puede hacer nada con ella y tiene que dejar que "suba" en la cadena de llamado, con lo cual la firma del metodo va creciendo y creciendo teniendo un monton de excepciones que dejan de tener sentid:....
}
Lo mas recomendable es crear una jerarquia de clases que herede de RuntimeException y dejar que la excepcion suba sola hasta el inicio del hilo ( o en alguna clase intermedia que tenga precisamente el manejo de todas las excepciones ) justo como dice Enrique.
primero
Entonces quieres decir que el primer tipo (de los que describes) es el que no tiene mucho sentido, es decir, extender Exception.
problemas
Y ese problema que describe Oscar con tener que declarar un montón de excepciones en la firma de tu método es lo de menos. Hay dos casos que en lo personal me repatea encontrar:
1. Cuando no quiero manejar una excepción dentro de un método que estoy extendiendo en una interfaz. Por ejemplo si estoy implementando
Runnable
, mi métodorun()
debe cachar cualquierException
porque no la puedo declarar en elthrows
del método porque eso altera su firma y el compilador no lo permite. Afortunadamente existe la anotación@SneakyThrows
en Lombok que permite darle la vuelta a este problema.2. Cuando extiendo un método que algún genio de Java declaró como
throws Exception
, no me queda de otra más que ponerle lo mismo, aunque no arroje nada realmente. Y cuando lo invoco pues ya me obligaron a aplicar el patrón Pokemon. Sí, commons-pool, estoy hablando de ti....Diseño de excepciones
Sin dudas diseñar las excepciones es también un tema porque estoy muy de acuerdo con que no todas las excepciones deben ser cachadas pero también no deben de ser todas tratadas hasta que veamos que falló algo en la ejecución. Quizas es que tengo una mala o mas bién incompleta teoria de las excepciones donde típicamente definia DataException, BusinessException y tan tan y una que otra hija de alguna de las anteriores (hacia el front no aplicaba porque tenia reglas de validación). El punto aqui es el diseño de las excepciones, no todas deben ser tratadas hasta que nos haya brincado en la ejecución pero igual es horrible tener que manejar varias excepciones cachadas en las implementaciones.
Ayer modificaba esta clase a la que le incluí
KeyException
pero ahora la bronca es que no me gustaria que esa excepcion se propague por todo el sistema sino que muera en la siguiente capa de implementación para dar lugar a una excepcion mas ad hoc.Creo que eso seria bronca de quien utilice el método no? pero apoco no seria lindo demarcar el alcance de una excepcion en parte para obligar a que hasta cierto nivel (de capa) se escriba código contingente.Le eché un lente a Exception Handling Antipattern pero no encontré nada reelevante, en cambio encontré las Effective Java Exceptions para entender un poco mejor.
Exception
Que tus excepciones no sean checadas no significa que no las puedas cachar o declarar. Esto es perfectamente válido:
//constructores
}
//mas abajo en otra clase
void metodo() throws PelationException {
//codigo
}
//y en otra parte, suponiendo que extiendes Runnable
@Override void run() {
metodo(); //valido
try {
metodo();
} catch (PelationException ex) {
//sólo debes hacer try-catch si realmente vas a hacer algo al respecto
}
}
La ventaja sobre las excepciones checadas es que no te obligan a hacer el try-catch. En este ejemplo, puedes invocar
metodo
sin tener que cachar su excepción pero tampoco tienes que cascadear elthrows
(cosa que no podrías hacer si quisieras, en elrun
).El compilador de Groovy por ejemplo no te obliga a cachar ninguna excepción; ninguna excepción se considera checada en Groovy. Scala y Ceylon tratan de la misma manera las excepciones: no es forzoso cacharlas, ni declarar que se arrojan, pero puedes hacerlo y de hecho es muy recomendable declarar las excepciones que puede arrojar un método, para que alguien las pueda cachar cuando lo invoca, si es que realmente le toca manejarla.
Duda con excepciones
java AddArguments uno 8 tres cuatro
me genera las salidas movidas es decir no coresponden con el orden en que se capturan las excepciones ejemplo:
Cadena sin formato de numero
8
Cadena sin formato de numero
Cadena sin formato de numero
o asi:
8
Cadena sin formato de numero
Cadena sin formato de numero
Cadena sin formato de numero
o asi:
Cadena sin formato de numero
Cadena sin formato de numero
Cadena sin formato de numero
8
aqui dejo el codigo:
public static void main(String args[]) {
int sum = 0;
for (int i = 0; i < 4; i++) {
try {
sum += Integer.parseInt(args[i]);
} catch (NumberFormatException obj2) {
System.err.println("Cadena sin formato de numero");
}
}
System.out.println("Sum = " + sum);
}
}
.println es Synchronized
Es que cuando imprimes con
System.out.println(String mensaje)
lo que está pasando es que el hilo que lo ejecuta se queda jetón porque al ser un métodosynchronized
se establece un candado que se liberará cuando termine su ejecución, mientras tanto, en el buclefor
estás solicitando la impresión de mas mensajes pero como elprintln()
aun está ejecutandose entonces la maquina virtual espera a que termine para ejecutar el siguiente y el siguiente y el siguiente.Si en vez de imprimirlo directamente pones los mensajes en un array (por ejemplo), notarás que "lo extraño" no sucede al arrojar las excepciones.
Tomando tu código, quedaria así:
int sum = 0;
for (int i = 0; i < 4; i++) {
try {
sum += Integer.parseInt(args[i]);
} catch (NumberFormatException obj2) {
messages[i] = "Cadena sin formato de numero";
}
}
for (String message : messages) {
System.out.println(message);
}
System.out.println("Sum = " + sum);
no es synchronized
println
no es synchronized, no está pasando nada de lo que dice java.daba.doo (tu aplicación ni siquiera es multi-hilo para empezar). "Lo raro" es porque el mensaje que imprimes cuando cachas la excepción lo estás imprimiendo a STDERR y la suma la estás imprimiendo a STDOUT. Aunque se vean igual en la consola, son dos streams distintos.Una solución simple es imprimir el mensaje del catch a STDOUT también. Con eso ya todo va a salir en el orden correcto (el 8 al final).
Ah pues si...
Oooops, no habia notado que estaba usando err y out. Efectivamente me fui a un contexto concurrente. Por cierto, segun el código de
java.io.PrintStream
(en jdk1.6.0_43) indica que println(...) efectivamente SI es synchronized.synchronized (this) {
print(x);
newLine();
}
}
(que por cierto, da igual que
public synchronized void println(String x)
por eso mencioné que es sincronizado)Aunque analizando mejor el asunto, el hecho de que sea
synchronized
es mas bien para que no se traslape el texto si se llega a invocar al mismo tiempo desde dos o mas hilos.Una disculpa por el mal diagnostico!
doc
En dónde dice que es synchronized? Ah viste fuentes? porque el javadoc no dice nada (yo revisé javadoc 6 y 7 antes de postear)
Gracias
Si bueno pense que como ambos son definidos como PrintStream (err y out) tenian la misma logica, pero desde ahora se que no, ya cambie el err para ver que resultados obtenia al usar ese stream y en algunas consolas cambia hasta de color el texto a rojo (reportando error), otras como el ms-dos no hacen nada las huevonas jajaja.
Cadena sin formato de numero
Cadena sin formato de numero
Cadena sin formato de numero
8
Entonces el println es thread-safe (no se si aplique el termino para lo de syncronized) pero el problema era por la diferencia entre streams out y err, puesto que no genero ningun otro hilo mas que el de main gracias de todas formas a java.dava.doo y por supuesto a ezamudio.
Y el usuario apa?
Existe una parte importante en el manejo del error y es ¿que se le va a mostrar al usuario?, ¿Se le va indicar que se comunico la app con el servidor, pero el servidor no hizo nada?, ¿Se le va a decir al usuario que tiene poco cerebro por no saber que algunos elementos de la division no pueden contener ceros?, ¿O simplemente se cerrara la aplicacion sin decir adios?, o peor aun, ¿se mostrara la pantalla sin informacion? o mas peor :P ¿no se le dira al usuario que se "medio hizo" la transaccion?
Eh?
Si te refieres a los
Error
como subclase deThrowable
no hay mucho que hacer, de hecho ese tipo es Fatal. Si quisiste referirte a error como una subclase deException
(como el caso en el que indicasArithmeticException
) mas bien debe ser una regla de negocio ¿Qué hacer en caso de error?Si estas persistiendo varios numeros y en uno de ellos el cociente no pudo definirse porque el denominador es 0, pos para empezar eso se tiene que validar. No esperes a que truene para saber que hacer. Si el usuario te dice que dividas A/B y de repente te sale un error de la VM indicando que el codigo no puede ejecutarse porque los datos no son aptos para tal operacion... ¿Quien es mas bobo?.
Cuando estas en etapas de pruebas muchos testers ponen datos estupidos pero no significa que ellos lo sean (en la mayria de los casos), simplemente quieren asegurarse que el código que escribiste es lo menos defectuoso posible. Si hay errores y si eres bueno programando seguro serán huecos en las reglas de negocio, de otra forma es señal de que hay que mejorar tu código.
En conclusión, creo que esos ejemplos que mencionas son mas bien cuestiones de negocio, no tanto broncas de mecanismo de las Excepciones en el lenguaje. Son pocas las excepciones que creo que si es necesario esperar a que ocurran para aceptarlas como validación, por ejemplo, si intentas convertir de un string a número, solamente hasta que salió el
NumberFormatException
nos damos cuenta que no es un string que representa a un numero. Pero por ejemplo divisiones entre 0, seguro se puede validar sin necesitad de una excepcion.un clasico mas de checked vs unchecked exceptions
Regla simple, citando a Bloch en Effective Java
Usa checked exceptions para una condición recuperable y runtime exceptions para errores de programación
A)Checked. Si en mi código se que puede haber bronca y quiero que el que lo vaya usar lo sepa pongo algo que sea Throwable.
B) Unchecked. Realmente me vale un cacahuate si lo quiere cachar o validar. Si detecto algun mal uso de mi código (por ejemplo valor de un parametro inadecuado) aviento un RuntimeException.
Todo esto sigue siendo debatible incluso hay lenguajes que quitaron los checked exception de su especificacion por ser demasiado ruidosos(C#, Scala, ¿algun otro?).
Link interesate http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html
no
A) checked - usas una subclase de Exception (no uses directamente Exception).
B) unchecked - ahí sí, usas una subclase de RuntimeException (o directamente RuntimeException).
Groovy convierte todas las excepciones a RuntimeException. Nunca es necesario que caches nada si no quieres.
Ceylon igual, todas las excepciones son de tiempo de ejecución, nunca te obliga a cachar algo.
Era una cuestion pedagogica
Pues si era un ejemplo pedagogico y como mencionan tanto NumberFormatException como ArithmeticException heredan de RuntimeException (no checadas o no marcadas) y segun entiendo este tipo de excepciones no es necesario capturarlas o no deberiamos siguiendo buenas practicas (no se si sea o no tan buena practica) ya que este tipo de excepciones son porque hay errores en la lógica de nuestros programas provocados por uno mismo.
Aunque bueno en mi código original que repito era pedagogico no dividian nada o si ? podria sugerir colocar un if y la comprobacion de la division entre 0 (solo cuando sean enteros) o algo mas adecuado un assert que compruebe que el divisor es diferente de 0 y ya si me da el AssertionError pues ya me chingue y a corregir el bug del programa.
El caso 2 que escribes no es necesario enrique
Cuando anulas (override) un método que heredas de una clase y ese método dispara una excepción, NO estás obligado a que el nuevo método dispare excepción alguna. No violas el contrato que establece la clase base. Lo contrario, anular un método que no dispara excepciones con una nuevo método que sí las dispara se prohíbe pues en ese caso estás violando el contrato que establece la clase base.
Si un objeto de la nueva clase es visto por una referencia a la clase base, tendrás que try/catch, aunque el nuevo método no dispare la excepción.
cierto
Lo acabo de probar, es correcto; nunca me había fijado que se puede ocultar la excepción en las subclases, obvio siempre y cuando no se invoque al método de la clase padre porque entonces se debe declarar la excepción, pero ya es por el método invocado y no por el que se extiende.
Esto es útil al momento de implementar la subclase, pero no tan útil al momento de utilizarla ya que si se codifica contra la clase padre (o interfaz por lo general), pues hay que cachar la excepción, aunque nunca se arroja en la subclase utilizada al momento de la ejecución...
Es LSP
En general, si deseas que la jerarquía de clases cumpla con LSP, entonces una subclase NO debe imponer ninguna restricción sobre las condiciones que establece la clase base, pero si puede relajar esas condiciones.