java - ejemplo - illegalstateexception
¿Cuándo debería lanzarse IllegalArgumentException? (6)
Me preocupa que esta sea una excepción de tiempo de ejecución, por lo que probablemente debería usarse con moderación.
Caso de uso estándar:
void setPercentage(int pct) {
if( pct < 0 || pct > 100) {
throw new IllegalArgumentException("bad percent");
}
}
Pero parece que forzaría el siguiente diseño:
public void computeScore() throws MyPackageException {
try {
setPercentage(userInputPercent);
}
catch(IllegalArgumentException exc){
throw new MyPackageException(exc);
}
}
Para volver a ser una excepción marcada.
De acuerdo, pero vamos con eso. Si da una mala entrada, obtiene un error de tiempo de ejecución. Entonces, en primer lugar, esa es una política bastante difícil de implementar de manera uniforme, porque podría tener que hacer la conversión opuesta:
public void scanEmail(String emailStr, InputStream mime) {
try {
EmailAddress parsedAddress = EmailUtil.parse(emailStr);
}
catch(ParseException exc){
throw new IllegalArgumentException("bad email", exc);
}
}
Y lo que es peor: mientras se comprueba 0 <= pct && pct <= 100
se espera que el código del cliente lo haga estáticamente, esto no es así para datos más avanzados como una dirección de correo electrónico o algo que se debe comparar con una base de datos , por lo tanto, en general, el código del cliente no puede validar previamente.
Entonces, básicamente, lo que estoy diciendo es que no veo una política coherente y significativa para el uso de IllegalArgumentException
. Parece que no debería usarse y debemos mantener nuestras propias excepciones marcadas. ¿Cuál es un buen caso de uso para lanzar esto?
Como se especifica en el tutorial oficial de Oracle, establece que:
Si es razonable esperar que un cliente se recupere de una excepción, conviértalo en una excepción marcada. Si un cliente no puede hacer nada para recuperarse de la excepción, conviértalo en una excepción no verificada.
Si tengo una Aplicación interactuando con la base de datos usando JDBC
, Y tengo un método que toma el argumento como el int item
y el double price
. El price
del artículo correspondiente se lee de la tabla de la base de datos. Simplemente multiplico el número total de item
comprados con el valor del price
y devuelvo el resultado. Aunque siempre estoy seguro de que al final (Aplicación final), el valor del campo de precios en la tabla nunca podría ser negativo. Pero, ¿qué ocurre si el valor del precio es negativo ? Muestra que hay un problema serio con el lado de la base de datos. Tal vez la entrada incorrecta del precio por parte del operador. Este es el tipo de problema que la otra parte de la aplicación que llama a ese método no puede anticipar y no puede recuperarse de él. Es un BUG
en su base de datos. Entonces, IllegalArguementException()
debería lanzarse en este caso, lo que IllegalArguementException()
que the price can''t be negative
.
Espero haber expresado mi punto claramente ...
Cualquier API debe verificar la validez de cada parámetro de cualquier método público antes de ejecutarlo:
void setPercentage(int pct, AnObject object) {
if( pct < 0 || pct > 100) {
throw new IllegalArgumentException("pct has an invalid value");
}
if (object == null) {
throw new IllegalArgumentException("object is null");
}
}
Representan el 99,9% de las veces errores en la aplicación porque están solicitando operaciones imposibles, por lo que al final son errores que deberían bloquear la aplicación (por lo que es un error no recuperable).
En este caso y siguiendo el enfoque de falla rápida, debe dejar que la aplicación finalice para evitar corromper el estado de la aplicación.
Cuando se habla de "mala entrada", debe considerar de dónde viene la entrada.
Es la entrada ingresada por un usuario u otro sistema externo que usted no controla, usted debe esperar que la entrada sea inválida y siempre validarla. Está perfectamente bien lanzar una excepción marcada en este caso. Su aplicación debe ''recuperarse'' de esta excepción proporcionando un mensaje de error al usuario.
Si la entrada se origina en su propio sistema, por ejemplo, su base de datos, o alguna otra parte de su aplicación, debe poder confiar en que sea válida (debería haber sido validada antes de llegar allí). En este caso, está perfectamente bien lanzar una excepción no verificada, como IllegalArgumentException, que no se debe capturar (en general, nunca se deben capturar las excepciones no verificadas). Es un error del programador que el valor inválido llegó allí en primer lugar;) Necesita corregirlo.
El documento de API para IllegalArgumentException es:
Lanzado para indicar que un método ha pasado un argumento ilegal o inapropiado.
Al mirar cómo se usa en las bibliotecas jdk , yo diría:
Parece una medida defensiva para quejarse de una entrada obviamente mala antes de que la entrada pueda entrar en acción y hacer que algo falle a mitad de camino con un mensaje de error sin sentido.
Se usa para casos en los que sería demasiado molesto lanzar una excepción marcada (aunque aparece en el código java.lang.reflect, donde la preocupación por niveles ridículos de check-exception-throwing no es evidente).
Yo usaría IllegalArgumentException para hacer una revisión de argumentos defensivos de última hora en busca de utilidades comunes (tratando de mantener la coherencia con el uso de jdk), donde la expectativa es que un argumento malo es un error de programador, similar a un NPE. No lo usaría para implementar la validación en el código comercial. Ciertamente no lo usaría para el ejemplo del correo electrónico.
Lanzar excepciones de tiempo de ejecución "con moderación" no es realmente una buena política. Efectivo Java recomienda que use excepciones comprobadas cuando se pueda esperar razonablemente que la persona que realiza la llamada se recupere . (El error del programador es un ejemplo específico: si un caso particular indica un error del programador, debe lanzar una excepción sin marcar; usted quiere que el programador tenga un seguimiento de la pila donde ocurrió el problema lógico, no para tratar de manejarlo usted mismo).
Si no hay esperanzas de recuperación, puede usar excepciones sin marcar; no tiene sentido atraparlos, así que está perfectamente bien.
Sin embargo, no está 100% claro en su ejemplo, en cuyo caso este ejemplo está en su código.
Tratar IllegalArgumentException
como una comprobación de condiciones previas y considerar el principio de diseño: un método público debe conocer y documentar públicamente sus propias condiciones previas.
Estoy de acuerdo con que este ejemplo es correcto:
void setPercentage(int pct) {
if( pct < 0 || pct > 100) {
throw new IllegalArgumentException("bad percent");
}
}
Si EmailUtil es opaco , lo que significa que hay alguna razón para que las condiciones previas no se puedan describir al usuario final, entonces una excepción marcada es correcta. La segunda versión, corregida para este diseño:
import com.someoneelse.EmailUtil;
public void scanEmail(String emailStr, InputStream mime) throws ParseException {
EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}
Si EmailUtil es transparente , por ejemplo, tal vez sea un método privado propiedad de la clase en cuestión, IllegalArgumentException
es correcta si y solo si sus condiciones previas se pueden describir en la documentación de la función. Esta es una versión correcta también:
/** @param String email An email with an address in the form [email protected]
* with no nested comments, periods or other nonsense.
*/
public String scanEmail(String email)
if (!addressIsProperlyFormatted(email)) {
throw new IllegalArgumentException("invalid address");
}
return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
// Assumes email is valid
boolean parsesJustFine = true;
// Parse logic
if (!parsesJustFine) {
// As a private method it is an internal error if address is improperly
// formatted. This is an internal error to the class implementation.
throw new AssertError("Internal error");
}
}
Este diseño podría ir en cualquier dirección.
- Si las condiciones previas son caras de describir, o si la clase está destinada a ser utilizada por clientes que no saben si sus correos electrónicos son válidos, entonces use
ParseException
. El método de nivel superior aquí se llamascanEmail
quescanEmail
que el usuario final tiene la intención de enviar un correo electrónico no estudiado, por lo que es probable que sea correcto. - Si las condiciones previas se pueden describir en la documentación de la función, y la clase no
IllegalArgumentException
entrada no válida y, por lo tanto, se indica un error del programador, useIllegalArgumentException
. Aunque no está "marcado", el "control" se traslada al Javadoc que documenta la función, que se espera que el cliente cumpla.IllegalArgumentException
donde el cliente no puede decir que su argumento es ilegal de antemano es incorrecto.
Una nota sobre IllegalStateException : Esto significa que "el estado interno de este objeto (variables de instancia privadas) no puede realizar esta acción". El usuario final no puede ver el estado privado por lo que vagamente hablando tiene prioridad sobre IllegalArgumentException
en el caso en que la llamada del cliente no tenga forma de saber que el estado del objeto es inconsistente. No tengo una buena explicación cuando se prefiere sobre las excepciones comprobadas, aunque cosas como inicializar dos veces o perder una conexión de base de datos que no se recupera son ejemplos.