Domanda

Java consente di creare un intero nuovo sottotipo di Throwable, per esempio:

public class FlyingPig extends Throwable { ... }

Ora, molto raramente , posso fare qualcosa di simile:

throw new FlyingPig("Oink!");

e naturalmente altrove:

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

Le mie domande sono:

  • Si tratta di una cattiva idea? E se sì, perché?
    • Cosa potrebbe essere stato fatto per prevenire questo sottotipizzazione se si tratta di una cattiva idea?
    • Dato che non è prevenibile (per quanto ne so), che cosa potrebbe causare catastrofi?
  • Se questa non è una cattiva idea, perché no?
    • Come si può fare qualcosa utile del fatto che si può extends Throwable?

Proposto scenario # 1

Uno scenario in cui ero davvero la tentazione di fare qualcosa di simile ha le seguenti proprietà:

  • Il "evento" è qualcosa che accadrà alla fine. E ' atteso . E 'sicuramente non è un Error, e non c'è niente Exception-al riguardo quando si verifica.
    • Perché è atteso , ci sarà un catch aspettando. E non "scivolare" passato nulla. Non sarà "fuga" da qualsiasi tentativo di catch Exception generale e / o Error.
  • Il "evento" accade molto raramente .
  • Quando accade, di solito c'è una traccia dello stack profondo.

Quindi, forse, è chiaro ora quello che sto cercando di dire:. FlyingPig è il risultato di una esaustiva ricerca ricorsiva

L'oggetto da cercare esiste: è solo una questione di trovare nel grande mare che è lo spazio di ricerca. Il processo di ricerca sarà lunga, quindi il costo relativamente costoso di gestione delle eccezioni è trascurabile. Infatti, il tradizionale flusso di controllo costrutto alternativa di usare una bandiera boolean isFound può essere più costoso, perché deve essere controllato in modo continuo durante tutto il processo di ricerca, molto probabilmente a tutti i livelli della ricorsione. Questo controllo non riesce il 99,99% del tempo, ma è assolutamente necessario per propagare la condizione di terminazione. In un certo senso, mentre efficace , il controllo è inefficiente

Con un semplice throw-zione un FlyingPig quando si trova l'oggetto ricercato, non dovete ingombrare il codice con la gestione della bandiera boolean isFound. Non solo è il codice più pulito in proposito, ma può correre più veloce grazie a questa omissione.

Quindi, per riassumere, la scelta è tra questi due:

  • approccio di controllo del flusso tradizionale
    • Usa un boolean isFound, controllata continuamente
    • 99,99% del tempo, il controllo è un "rifiuto", perché sarebbe ancora false
    • Quando alla fine diventa true, è smettere di ricorsione e si deve fare in modo che si può correttamente rilassarsi alla chiamata iniziale.
  • approccio FlyingPig
    • Non perdete tempo con qualsiasi boolean isFound.
    • Se trovato, appena throw new FlyingPig(); di essa la atteso , quindi ci sarà un catch per esso.
    • Nessuna gestione della bandiera boolean, nessun controllo sprecata se avete bisogno di andare avanti, senza contabilità manualmente rilassarsi la ricorsione, ecc.

Domande:

  • È questa tecnica di (ab) usando un'eccezione valida? (C'è un nome per questo?)
  • Se valida, dovrebbe FlyingPig extends Throwable, o è Exception bene? (Anche se non c'è niente di eccezionale la sua situazione?)
È stato utile?

Soluzione

Direi che si tratta di una pessima idea. Un sacco di codice è implementato sul presupposto che se si cattura Error e Exception hai preso tutte le eccezioni possibili. E la maggior parte tutorial e libri di testo vi dirà la stessa cosa. Con la creazione di una sottoclasse diretta di Throwable si sta creando potenzialmente tutti i tipi di problemi di manutenzione e di interoperabilità.

mi viene in mente nessun buon motivo per estendere Throwable. Estendere Exception o RuntimeException posto.

Modifica -. In risposta alla scenario proposto del PO # 1

Le eccezioni sono un modo molto costoso di trattare con il controllo di flusso "normale". In alcuni casi, stiamo parlando migliaia di istruzioni in più eseguite per creare, lanciare e prendere un'eccezione. Se avete intenzione di ignorare accettati saggezza e di usare le eccezioni per il controllo del flusso non eccezionale, utilizzare un sottotipo Exception. Cercando di far finta qualcosa è un "evento" non una "eccezione" dichiarando è come un sottotipo di Throwable non sta per ottenere qualcosa.

Tuttavia, è un errore confondere un'eccezione con un errore, errore, sbagliato, qualunque sia. E non v'è nulla di sbagliato di rappresentare un "evento eccezionale che non è un errore, errore, sbagliato, o qualsiasi altra cosa" utilizzando una sottoclasse di Exception. La chiave è che l'evento deve essere eccezionale ; vale a dire fuori dal comune, succede molto di rado, ...

In sintesi, un FlyingPig non può essere un errore, ma non c'è ragione per non dichiararlo come un sottotipo di Exception.

Altri suggerimenti

  

E 'questa tecnica di (ab) usando un'eccezione valida? (C'è un nome per questo?)

A mio parere, non è una buona idea:

  • principio del minimo stupore , rende il codice più difficile da leggere, in particolare se non c'è niente eccezioni al riguardo.
  • Lanciare eccezione è un'operazione molto costosa in Java (potrebbe non essere un problema qui però).

non può essere utilizzato per il controllo di flusso . Se dovessi dare un nome questa tecnica, lo chiamerei un codice odore o un modello anti.

Vedi anche:

  

Se valida, dovrebbe FlyingPig estende Throwable, o è Exception bene? (Anche se non c'è niente di eccezionale la sua situazione?)

Ci potrebbero essere delle situazioni in cui si desidera cattura Throwable non solo Exception cattura ma anche Error ma è raro, la gente di solito non lo fanno cattura Throwable. Ma non riesco a trovare una situazione in cui si desidera due una sottoclasse di Throwable.

E penso anche che l'estensione Throwable non rende le cose sembrano meno "eccezioni al" , fanno sembrare peggio - che sconfigge del tutto l'intenzione.

Quindi, per concludere, se si vuole veramente buttare qualcosa, lanciare una sottoclasse di Exception.

Vedi anche:

Ecco un post sul blog da John Rose, un architetto di HotSpot:

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

Si tratta di "abuso" eccezioni per il controllo del flusso. Leggermente diverso caso d'uso, ma .. In breve, funziona davvero bene - se si Preallocare / clonare il vostro eccezioni per evitare tracce dello stack in fase di creazione.

Credo che questa tecnica è giustificabile se "nascosti" da parte dei clienti. IE, il vostro FlyingPig non dovrebbe mai essere in grado di lasciare la vostra libreria (tutti i metodi pubblici dovrebbero transitivamente garantire non buttarlo). Un modo per garantire questo sarebbe quello di fare un'eccezione controllata.

Credo che l'unica giustificazione per estendere Throwable è perché si vuole permettere alle persone di passare in callback che hanno catch (Exception e) clausole, e si desidera il risultato di essere ignorato da loro. Posso solo di comprare quel ...

L'annotazione org.junit.Test include la classe None che si estende Throwable e viene utilizzato come valore predefinito per il parametro expected nota.

Se si può giustificare ciò che distingue un FlyingPig a parte sia degli errori e delle eccezioni, in modo tale che non è adatto come una sottoclasse di entrambi, quindi non c'è niente di fondamentalmente sbagliato con la creazione di esso.

Il problema più grande che posso pensare è nel mondo pragmatico, a volte ci sono ragioni giustificabili per la cattura java.lang.Exception. Il tuo nuovo tipo di Throwable sta per volare a destra isolati passato try-catch che avevano ogni ragionevole aspettativa di sopprimere (o la registrazione, confezionamento, a prescindere) ogni possibile problema non fatale.

Sul rovescio della medaglia, se si sta facendo manutenzione su un vecchio sistema che è un giustamente sopprimere java.lang.Exception, si potrebbe imbrogliare intorno ad esso. (Supponendo che l'appello sincero per il momento di risolvere il problema in realtà correttamente è negato).

  

Come questa domanda si è evoluta, vedo   che ho frainteso il punto della   domanda iniziale, in modo da alcuni dei   altre risposte sono probabilmente più   rilevante. Lascerò questa risposta up   qui, dal momento che può essere ancora utile   altri che hanno una domanda simile,   e trovare questa pagina durante la ricerca di   una risposta alla loro domanda.

Non c'è niente di sbagliato con l'estensione Throwable per essere in grado di lanciare (e maniglia) eccezioni personalizzate. Tuttavia, si dovrebbe tenere i seguenti punti:

  • Uso della eccezione più specifica possibile è una grande idea. Esso permetterà al chiamante di gestire diversi tipi di eccezioni in modo diverso. La gerarchia delle classi della deroga è importante tenere a mente, così il vostro eccezione personalizzata dovrebbe estendere un altro tipo di Throwable che sia il più vicino al tuo eccezione possibile.
  • L'estensione Throwable potrebbe essere troppo alto livello. Prova l'estensione delle eccezioni o RuntimeException invece (o un'eccezione di livello inferiore che è più vicino alla ragione si sta gettando un'eccezione). Tenete a mente la differenza tra un RuntimeException e un'eccezione.
  • Una chiamata a un metodo che genera un'eccezione (o una sottoclasse di Exception) dovrà essere avvolto in un blocco try / catch che è in grado di trattare con l'eccezione. Questo è un bene per i casi in cui ci si aspetta cose vanno male, a causa di circostanze che possono essere fuori dal vostro controllo (ad esempio, la rete che è in basso).
  • Una chiamata a un metodo che lancia una RuntimeException (o una sottoclasse di esso) non ha bisogno di essere avvolto in un blocco try / catch in grado di gestire l'eccezione. (Certamente potrebbe essere, ma non ha bisogno di essere.) Questo è più per le eccezioni che in realtà non dovrebbe essere previsto.

Quindi, si supponga di avere le seguenti classi di eccezioni nel codice di base:

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

e alcuni metodi

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() { ... } 

Ora, si potrebbe avere un po 'di codice che chiama questi metodi come questo:

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

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

baz();

Si noti che quando chiamiamo bar(), possiamo solo prendere Pig, dal momento che entrambi FlyingPig e Swine estendono Pig. Questo è utile se si vuole fare la stessa cosa per gestire sia un'eccezione. Si potrebbe, tuttavia, gestirli in modo diverso:

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

Il Play! quadro usi qualcosa di simile per la gestione delle richieste. L'elaborazione della richiesta passa attraverso molti strati (routing, middleware, controllori, il rendering del modello) e allo strato finale il rendering HTML è avvolto in un throwable e gettato, che all'inizio strato più catture, scarta e invia al cliente. Quindi, nessuno dei metodi in molti strati coinvolti necessità di tornare in modo esplicito un oggetto di risposta, né hanno bisogno di avere un oggetto di risposta passato come argomento per essere propagate e modificato.

Sono po 'anonima sui dettagli. Si può guardare attraverso il codice di Play! quadro di riferimento per i dettagli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top