Pregunta

Java le permite crear todo un nuevo subtipo de Throwable, por ejemplo:

public class FlyingPig extends Throwable { ... }

Ahora, muy raramente , puedo hacer algo como esto:

throw new FlyingPig("Oink!");

y por supuesto en otra parte:

try { ... } catch (FlyingPig porky) { ... }

Mis preguntas son:

  • ¿Es esta una mala idea? Y si es así, ¿por qué?
    • ¿Qué podría haber hecho para prevenir esta subtipos si es una mala idea?
    • Ya que no es prevenible (por lo que yo sé), lo que podría dar lugar a catástrofes?
  • Si esto no es tan mala idea, ¿por qué no?
    • ¿Cómo puede hacer algo útil del hecho de que puede extends Throwable?

Propuesta escenario # 1

Un escenario donde estaba realmente tentado a hacer algo como esto tiene las siguientes propiedades:

  • El "evento" es algo que pasar con el tiempo. Es esperada . Es definitivamente no es un Error, y no hay nada Exception-al sobre cuando se produzca.
    • Debido a que es esperada , habrá un catch esperando. No lo hará "deslizamiento" pasado nada. No va a "escapar" de cualquier intento de catch Exception y / o Error general.
  • El "evento" sucede muy raramente .
  • Cuando sucede, por lo general hay un seguimiento de la pila de profundidad.

Así que tal vez es claro ahora lo que estoy tratando de decir:. FlyingPig es el resultado de una exhaustiva búsqueda recursiva

El objeto que se debe buscar existe: es sólo una cuestión de encontrar en el gran mar que es el espacio de búsqueda. El proceso de búsqueda será larga, por lo que el costo relativamente caros de manejo de excepciones es insignificante. De hecho, el tradicional flujo de control construcción alternativa de utilizar una bandera boolean isFound puede ser más caro, ya que tiene que ser revisado continuamente durante todo el proceso de búsqueda, más probable es que en todos los niveles de la recursividad. Esta comprobación fallará el 99,99% de las veces, pero es absolutamente necesario para propagar la condición de terminación. En cierto modo, mientras que efectiva , el cheque es ineficiente

Simplemente throw-ing un FlyingPig cuando se encuentra el objeto buscado, que no tiene que recargar el código con la gestión de la bandera boolean isFound. No sólo es el código más limpio en ese sentido, pero puede correr más rápido debido a esta omisión.

Entonces, para resumir, la elección es entre estos dos:

  • enfoque de control de flujo tradicional
    • Utilice un boolean isFound, comprueba continuamente
    • 99,99% de las veces, el cheque es un "desecho", porque todavía estaría false
    • Cuando finalmente se convierte en true, se deja de manera recursiva y usted tiene que asegurarse de que pueda desenrollar correctamente a la llamada inicial.
  • enfoque FlyingPig
    • No se moleste con cualquier boolean isFound.
    • Si lo encuentra, simplemente throw new FlyingPig(); es de esperada , por lo que habrá un catch por ello.
    • No hay gestión de la bandera boolean, sin verificación desperdiciada si es necesario para seguir adelante, no hay contabilidad para desenrollar manualmente la recursividad, etc.

Preguntas:

  • ¿Es esta técnica de (ab) uso de excepción válida? (¿Hay un nombre para él?)
  • Si es válida, debe FlyingPig extends Throwable, o bien sólo es Exception? (A pesar de que no hay nada excepcional en sus circunstancias?)
¿Fue útil?

Solución

Yo diría que es una muy mala idea. Una gran cantidad de código se implementa en el supuesto de que si se captura Error y Exception usted ha cogido todas las excepciones posibles. Y la mayoría de los tutoriales y libros de texto le dirá lo mismo. Mediante la creación de una subclase directa de Throwable está potencialmente crear todo tipo de problemas de mantenimiento e interoperabilidad.

No puedo pensar en ninguna buena razón para extender Throwable. Extender Exception o RuntimeException lugar.

Editar -. En respuesta al escenario propuesto por el PO # 1

Las excepciones son una forma muy costosa de tratar con control de flujo "normal". En algunos casos, estamos hablando miles de las instrucciones ejecutadas adicionales para crear, lanzar y atrapar una excepción. Si se va a ignorar la sabiduría y el uso de excepciones aceptadas para el control de flujo no excepcional, utilizar un subtipo Exception. Tratando de fingir algo es un "evento", no una "excepción" al declarar es como un subtipo de Throwable no va a lograr nada.

Sin embargo, es un error confundir una excepción con un error, error, mal, lo que sea. Y hay nada malo , con lo que supone un "supuesto excepcional de que no es un error, error, mal, o lo que sea" por medio de una subclase de Exception. La clave es que el evento debe ser excepcional ; es decir, fuera de lo común, ocurriendo con muy poca frecuencia, ...

En resumen, un FlyingPig no puede ser un error, pero eso no es razón para no declararlo como un subtipo de Exception.

Otros consejos

¿Es esta técnica de (ab) uso de excepción válida? (¿Hay un nombre para él?)

En mi opinión, no es una buena idea:

  • Se viola el principio de mínima sorpresa , hace que el código sea más difícil de leer, especialmente si no hay nada de excepciones al respecto.
  • Lanzar excepción es una operación muy costosa en Java (podría no ser un problema aquí, sin embargo).

no puede utilizarse para el control de flujo . Si tuviera que nombrar esta técnica, yo diría que es un olor código o un patrón contra.

Ver también:

Si es válida, debe FlyingPig extiende Throwable, o es Exception bien? (A pesar de que no hay nada excepcional en sus circunstancias?)

Puede haber situaciones en las que desee captura Throwable no sólo Exception captura sino también Error pero es raro, la gente por lo general no lo hacen capturas Throwable. Pero no llego a encontrar una situación en la que desea tiro una subclase de Throwable.

Y también creo que la ampliación Throwable no hace que las cosas se ven menos "excepción-al" , lo hacen parecer peor - lo que contradice totalmente la intención.

Así que para concluir, si realmente quiere tirar algo, tirar una subclase de Exception.

Ver también:

Esta es una entrada de blog de John Rose, un arquitecto de HotSpot:

http://blogs.oracle.com/jrose/entry/longjumps_considered_inexpensive

Se trata de "abusar" excepciones para control de flujo. Ligeramente diferentes casos de uso, pero .. En pocas palabras, funciona muy bien - si usted asignar previamente / clon sus excepciones para evitar que trazas de la pila se está creando.

creo que esta técnica es justificable si "oculta" de los clientes. IE, su Flyingpig nunca debe ser capaz de salir de su biblioteca (todos los métodos públicos deben garantizar no transitiva para tirarlo). Una forma de garantizar esto sería para que sea una excepción comprobada.

Creo que la única justificación para extender Throwable es porque quiere permitir que las personas pasan en las devoluciones de llamada que tienen catch (Exception e) cláusulas, y desea que su resultado ser ignorados por ellos. Puedo sólo de comprar ese ...

La anotación org.junit.Test incluye la clase None que se extiende Throwable y se utiliza como el valor por defecto para el parámetro expected anotación.

Si se puede justificar lo que establece un Flyingpig tanto, aparte de error y de excepción, de tal manera que no es adecuado como una subclase de cualquiera, entonces no hay nada fundamentalmente malo en la creación de la misma.

El mayor problema que puedo pensar es en el mundo pragmático, a veces hay razones suficientes para atrapar java.lang.Exception. Su nuevo tipo de throwable va a volar últimos bloques try-catch derecho que tenían todas las expectativas razonables de suprimir (o la tala, envoltura, lo que sea) todos los problemas posibles no fatal.

En el otro lado, si está haciendo el mantenimiento de un sistema antiguo que es un justificadamente suprimir java.lang.Exception, que podría engañar a su alrededor. (Suponiendo que la apelación sincera de tiempo para realmente solucionarlo adecuadamente es denegada).

A medida que esta cuestión ha evolucionado, veo que no he entendido bien el punto de la pregunta original, por lo que algunas de las otras respuestas son probablemente más pertinente. Voy a dejar esta respuesta hasta aquí, ya que todavía puede ser útil otros que tienen una pregunta similar, y encontrar esta página durante la búsqueda de una respuesta a su pregunta.

No hay nada malo con la extensión de Throwable que sea capaz de lanzar (y mango) excepciones personalizadas. Sin embargo, usted debe tener los siguientes puntos en cuenta:

  • Uso de la mayor parte posible excepción específica es una gran idea. Esto permitirá a la persona que llama para manejar diferentes tipos de excepciones de manera diferente. La jerarquía de clases de excepción es importante tener en cuenta, por lo que su excepción personalizada debe extenderse otro tipo de Throwable que es tan cerca de su excepción de lo posible.
  • La extensión de Throwable podría ser demasiado alto nivel. Trate de excepción que se extiende RuntimeException o en su lugar (o una excepción de nivel inferior que está más cerca de la razón por la que está lanzando una excepción). Tenga en cuenta la diferencia entre un RuntimeException y una excepción.
  • Una llamada a un método que produce una excepción (o una subclase de Exception) tendrá que ser envuelto en un bloque try / catch que es capaz de hacer frente a la excepción. Esto es bueno para los casos cuando se espera que las cosas van mal, debido a circunstancias que pueden estar fuera de su control (por ejemplo, la red de estar abajo).
  • no necesita una llamada a un método que lanza una RuntimeException (o una subclase de ella) para ser envuelto en un bloque try / catch que puede manejar la excepción. (Sin duda, podría ser, pero no tiene que ser.) Esto es más excepciones que realmente no se debe esperar.

Así, supongamos que tiene las siguientes clases de excepción en su base de código:

public class Pig extends Throwable { ... }
public class FlyingPig extends Pig { ... }
public class Swine extends Pig { ... }
public class CustomRuntimeException extends RuntimeException { ... }

y algunos métodos

public void foo() throws Pig { ... }
public void bar() throws FlyingPig, Swine { ... }
// suppose this next method could throw a CustomRuntimeException, but it
// doesn't need to be declared, since CustomRuntimeException is a subclass
// of RuntimeException
public void baz() { ... } 

Ahora, usted podría tener un código que llama a estos métodos como éste:

try {
    foo();
} catch (Pig e) {
    ...
}

try {
    bar();
} catch (Pig e) {
    ...
}

baz();

Tenga en cuenta que cuando llamamos bar(), sólo podemos coger Pig, ya que ambos FlyingPig y Swine extienden Pig. Esto es útil si desea hacer lo mismo para manejar bien la excepción. Se podría, sin embargo, manejar de manera diferente:

try {
    bar();
} catch (FlyingPig e) {
    ...
} catch (Swine e) {
    ...
}

El Juego! marco usos algo como esto para el manejo de las peticiones. El procesamiento de solicitud se pasa a través de muchas capas (enrutamiento, middleware, controladores, plantilla de representación) y en la capa final, el HTML representado es envuelto en una throwable y arrojado, que los mejores capa más capturas, se desenrolla y la envía a la cliente. Por lo que ninguno de los métodos en las muchas capas implicadas necesidad de volver explícitamente un objeto de respuesta, ni tampoco necesita tener un objeto respuesta pasado como parámetro a ser propagado y modificado.

Soy poco rara en los detalles. Usted puede mirar a través del código del juego! marco para más detalles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top