Pregunta ¿Cuándo debería lanzarse IllegalArgumentException?


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 peor, mientras revisas 0 <= pct && pct <= 100 se puede esperar 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 peor, algo que deba verificarse contra una base de datos, por lo tanto, en general, el código del cliente no puede validar previamente.

Así que, 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?


76
2018-03-04 18:34


origen


Respuestas:


El documento de API para IllegalArgumentException es:

Lanzado para indicar que un método ha pasado un argumento ilegal o inapropiado.

Desde 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.


57
2018-03-04 19:48



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 proviene de su propio sistema, p. 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 ahí). 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.


19
2018-03-04 20:06



Lanzar excepciones de tiempo de ejecución "con moderación" no es realmente una buena política. Efectivo Java recomienda usar excepciones comprobadas cuando la persona que llama se puede esperar razonablemente que 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.


10
2018-03-04 18:40



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.


5
2017-12-17 11:47



Como se especifica en el tutorial oficial de Oracle, establece que:

Si se puede esperar razonablemente que un cliente se recupere de una excepción,   convertirlo 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 double price. los price para el elemento correspondiente se lee de la tabla de la base de datos. Simplemente multiplico el número total de item comprado con el price valorar y devolver el resultado. Aunque siempre estoy seguro de que al final (Fin de la aplicación) el valor del campo de precio en la tabla nunca podría ser negativo. Pero, ¿qué sucede si sale el valor del precio? 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 tu base de datos Y entonces IllegalArguementException() debe arrojarse en este caso que afirmaría que the price can't be negative.
Espero haber expresado mi punto claramente ...


4
2018-03-04 19:06



Tratar IllegalArgumentException como un precondiciones verificar, 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 por la cual las condiciones previas no se pueden 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 es un método privado propiedad de la clase en cuestión, IllegalArgumentException es correcto 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 abc@xyz.com
 * 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, utilice ParseException. El método de nivel superior aquí se llama scanEmail que sugiere 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 intenta la entrada no válida y, por lo tanto, se indica un error del programador, utilice IllegalArgumentException. 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 donde la llamada del cliente no tiene manera 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.


0
2018-03-27 16:15