2013-01-23 25 views
37

I miei tipi di eccezione personalizzati sono case class?Le eccezioni devono essere classi del caso?

Sul lato positivo, ottengo estrattori.

Sul lato negativo, ottengo semantica di uguaglianza errata. Ma posso evitarlo ignorando equals.

Quindi ha senso, a livello concettuale, renderli case class es?

+6

@TravisBrown - "Tutto ciò che penso di sapere che la risposta deve essere ovvia e quindi non è costruttiva. " –

+0

Quello che sto cercando sarebbe una macro che crea solo un compagno di classe case con apply e unpply, ma senza tutto il resto, specialmente senza le restrizioni di ereditarietà. Questo sarebbe utile anche in altri posti. –

risposta

32

Questo è molto soggettivo, naturalmente, ma a mio parere è buona norma avere classi di eccezione come case classes. La ragione principale è che quando si cattura un'eccezione, si esegue la corrispondenza dei modelli e le classi dei casi sono molto più belle da utilizzare nella corrispondenza dei modelli. Ecco un esempio che prende advantadge della possibilità di utilizzare tutta la potenza di pattern matching in un blocco catch, quando si utilizza un'eccezione caso classe:

object IOErrorType extends Enumeration { 
    val FileNotFound, DeviceError, LockedFile = Value 
} 
case class IOError(message: String, errorType: IOErrorType.Value) extends Exception(message) 

def doSomeIO() { throw IOError("Oops, file not found!", IOErrorType.FileNotFound) } 

try { 
    doSomeIO() 
} catch { 
    case IOError(msg, IOErrorType.FileNotFound) => 
    println("File not found, please check the path! (" + msg + ")") 
} 

In questo esempio, abbiamo una sola eccezione, ma contiene un campo errorType per quando si desidera conoscere il tipo esatto di errore che si è verificato (di solito questo è modellato attraverso una gerarchia di eccezioni, non sto dicendo che questo è migliore o peggiore, l'esempio è solo illustrativo). Poiché la IOError è una classe di casi, posso semplicemente fare case IOError(msg, IOErrorType.FileNotFound) per rilevare l'eccezione solo per il tipo di errore IOErrorType.FileNotFound. Senza gli estrattori che otteniamo gratuitamente con le classi dei casi, dovrei rilevare l'eccezione ogni volta, e quindi revocarla nel caso in cui non sia realmente interessata, che è decisamente più prolissa.

Si dice che le classi di casi forniscono una semantica di uguaglianza errata. Io non la penso così , come lo scrittore della classe di eccezioni arriva a decidere quale semantica dell'uguaglianza ha senso. Dopotutto, quando si cattura un'eccezione, il blocco catch è il punto in cui si decidono quali eccezioni catturare di solito in base al tipo da solo, ma potrebbero essere basate sul valore dei suoi campi o qualsiasi altra cosa, come nel mio esempio. Il punto è che la semantica di uguaglianza della classe di eccezione ha poco a che fare con questo.

17

Un linguaggio comune che si perde facendo eccezioni case classes è il modello di creazione di una sottoclasse di eccezioni con sottoclasse utilizzata per indicare una maggiore specificità della condizione di errore. Le classi di casi non possono essere sottoclasse.

+3

Buon punto. Ma se necessario è ancora possibile creare una gerarchia di tipi di eccezioni astratte (tratti o classi astratte) e avere tutti i tipi di foglia come classi caso. Ciò richiede di "possedere" l'intera gerarchia, o in altre parole che il codice client non dovrebbe estendere le tue classi di eccezioni. In caso contrario, utilizzare le classi case per le eccezioni potrebbe non essere l'idea migliore. –

+2

Sebbene sia possibile definire un tratto comune, le classi di casi –

+3

possono essere suddivise in sottoclassi. L'unica restrizione è che la sottoclasse non può essere una classe del caso. –

1

Mi piace la risposta di Régis Jean-Gilles. Ma se avete un buon motivo per non fare una classe caso (vedi risposta di Dave Griffith), è possibile raggiungere un 'lo stesso che nel campione di cui sopra con una classe normale e unapply:

object IOErrorType extends Enumeration { 
    val FileNotFound, DeviceError, LockedFile = Value 
} 
object IOError { 
    def unapply(err: IOError): Option[(String, IOErrorType.Value)] = Some(err.message, err.errorType) 
} 
class IOError(val message: String, val errorType: IOErrorType.Value) extends Exception(message) 

def doSomeIO() { throw new IOError("Oops, file not found!", IOErrorType.FileNotFound) } 

try { 
    doSomeIO() 
} catch { 
    case IOError(msg, IOErrorType.FileNotFound) => 
    println("File not found, please check the path! (" + msg + ")") 
} 
Problemi correlati