programar - scala introduccion
¿Deberían las excepciones ser clases de casos? (3)
¿Deberían mis tipos de excepciones personalizados ser case class
?
En el lado positivo, obtengo extractores.
En el lado negativo, obtengo una semántica de igualdad incorrecta. Pero puedo evitar eso anulando equals
.
Entonces, ¿tiene sentido, a nivel conceptual, convertirlos en case class
?
Esto es muy subjetivo, por supuesto, pero en mi opinión es una buena práctica tener clases de excepción como clases de casos. La principal razón es que cuando se detecta una excepción, se está haciendo una coincidencia de patrones, y las clases de casos son mucho más fáciles de usar en la coincidencia de patrones. Aquí hay un ejemplo que aprovecha la capacidad de usar toda la potencia de la coincidencia de patrones en un bloque catch, cuando se utiliza una excepción de clase de caso:
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 + ")")
}
En este ejemplo, tenemos solo una excepción, pero contiene un campo errorType
para cuando quiere saber el tipo de error exacto que ocurrió (generalmente esto se modela a través de una jerarquía de excepción, no estoy diciendo que esto sea mejor o peor, el ejemplo es solo ilustrativo). Como IOError
es una clase de caso, simplemente puedo hacer el case IOError( msg, IOErrorType.FileNotFound )
para capturar la excepción solo para el tipo de error IOErrorType.FileNotFound
. Sin los extractores que obtenemos de forma gratuita con las clases de casos, tendré que atrapar la excepción cada vez, y luego volver a lanzarla en caso de que no esté realmente interesado, lo cual es definitivamente más detallado.
Usted dice que las clases de casos le dan una semántica de igualdad incorrecta. No lo creo. Usted , como llega a ser el escritor de la clase de excepción, decide qué semántica de igualdad tiene sentido. Después de todo, cuando atrapa una excepción, el bloque catch es donde decide qué excepciones capturar, generalmente basándose en el tipo solo, pero podría basarse en el valor de sus campos o lo que sea, como en mi ejemplo. El punto es que la semántica de igualdad de la clase de excepción tiene poco que ver con eso.
Me gusta la respuesta de Régis Jean-Gilles. Pero si tiene una buena razón para no hacer una clase de caso (vea la respuesta de Dave Griffith), puede archivar lo mismo que en el ejemplo anterior con una clase normal y desaplicar:
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 + ")")
}
Una expresión común que se pierde al crear clases de casos de excepciones es el patrón de creación de una jerarquía de excepciones de subclase con subclases usadas para indicar una mayor especificidad de la condición de error. Las clases de casos no se pueden subclasificar.