¿Es mejor usar afirmar o IllegalArgumentException para los parámetros de método requeridos?

88

En Java, ¿cuál es más recomendable y por qué? Ambos tipos arrojarán excepciones, por lo que, en ese sentido, manejarlos es lo mismo. assertes un poco más corto, pero no estoy seguro de cuánto importa.

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

vs

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}
Daenyth
fuente
Prefiero un simple en su obj.hashCode()lugar ;-)
Marco

Respuestas:

119

¡TENER CUIDADO!

Las aserciones se eliminan en tiempo de ejecución a menos que especifique explícitamente "habilitar aserciones" al compilar su código. Las aserciones de Java no se deben usar en el código de producción y se deben restringir a métodos privados (consulte Excepción frente a aserción ), ya que se espera que los métodos privados sean conocidos y utilizados solo por los desarrolladores. También assertarrojará AssertionError que Errorno se extiende Exception, y que normalmente indica que tiene un error muy anormal (como "OutOfMemoryError" que es difícil de recuperar, ¿no es así?) Que no se espera que pueda tratar.

Elimine el indicador "habilitar aserciones" y verifique con un depurador y verá que no pisará la llamada de lanzamiento IllegalArgumentException ... ya que este código no se ha compilado (nuevamente, cuando se elimina "ea")

Es mejor usar la segunda construcción para métodos públicos / protegidos, y si desea hacer algo en una línea de código, hay al menos una forma que conozco. Yo personalmente uso la primavera Marco 's Assertclase que tiene unos métodos para comprobar que los argumentos y tirar 'IllegalArgumentException' en caso de fallo. Básicamente, lo que haces es:

Assert.notNull(obj, "object was null");

... que de hecho ejecutará exactamente el mismo código que escribió en su segundo ejemplo. Hay algunos otros métodos útiles, como hasText, hasLengthallí.

No me gusta escribir más código del necesario, así que estoy contento cuando reduzco el número de líneas escritas en 2 (2 líneas> 1 línea) :-)

Jalayn
fuente
¡Ah, olvidé que las afirmaciones se eliminaron! Gran respuesta. Esperaré un poco para ver si entra algo más, luego lo aceptaré :)
Daenyth
2
Tenga en cuenta que no hay ningún indicador que elimine las aserciones en el momento de la compilación (aunque pueden eliminarse mediante la compilación condicional ). Las aserciones están deshabilitadas en tiempo de ejecución de forma predeterminada (creo que la JVM las trata como NOOP), pero se pueden habilitar a través java -eay mediante programación. @Jalayn Creo que tener afirmaciones en el código de producción es perfectamente válido, ya que son útiles para la depuración en el campo
Justin Muller
@Jalayn, -1. El compilador no elimina el código de aserción. Aunque no se ejecutarán a menos que lo haga cmd java -ea.
Pacerier
55
no es necesario el marco de Spring cuando puedes usarloObjects.requreNonNull
2017
46

Necesitas usar una excepción. Usar una afirmación sería un mal uso de la función.

Las excepciones no marcadas están diseñadas para detectar errores de programación de los usuarios de su biblioteca, mientras que las aserciones están diseñadas para detectar errores en su propia lógica. Estos son problemas separados que no deben mezclarse.

Por ejemplo, una afirmación

assert myConnection.isConnected();

significa "Sé que cada ruta de código que conduce a esta afirmación garantiza que myConnectionesté conectado; si el código anterior no pudo obtener una conexión válida, debería haber lanzado una excepción o regresar antes de llegar a este punto".

Por otro lado, un cheque

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

significa que "Llamar a mi biblioteca sin establecer una conexión es un error de programación".

dasblinkenlight
fuente
1
Esta información es realmente útil, pero acepto la de Jalayn, ya que señala cómo podría introducir un error con el método de aserción.
Daenyth
44
punto excelente "Las excepciones no verificadas están diseñadas para detectar errores de programación de los usuarios de su biblioteca, mientras que las aserciones están diseñadas para detectar errores en su propia lógica. Estos son problemas separados que no deben mezclarse".
Asim Ghaffar
Es cierto, pero esto supone que OP está escribiendo una biblioteca. Si su código es solo para uso interno, una afirmación es aceptable.
user949300
11

Si está escribiendo una función que no permite nullun valor de parámetro válido, debe agregar la @Nonnullanotación a la firma y usarla Objects.requireNonNullpara verificar si el argumento es válido nully arrojar un NullPointerExceptionsi es así. La @Nonnullanotación es para documentación y proporcionará advertencias útiles en el momento de la compilación en algunos casos. No impide que nullse pase en tiempo de ejecución.

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

Actualización: la @Nonnullanotación no forma parte de la biblioteca estándar de Java. En cambio, existen numerosos estándares competidores de bibliotecas de terceros (consulte ¿Qué anotación Java @NotNull debo usar? ). Eso no significa que sea una mala idea usarlo, solo que no es estándar.

rebelde
fuente
Gracias por señalar Objects.requireNonNull(), que era nuevo para mí. ¿Sabes si hay un método similar "requireTrue ()" o "requireEqual ()" en alguna parte? Nada en contra de Spring's Assert, pero no todo el mundo está usando eso.
user949300
@ user949300 Objects.requireNonNull()es para la validación de argumentos. Si se requiere que un argumento sea trueo sea igual a algo, entonces el argumento no tiene sentido. Para los casos de error que no sean argumentos ilegales, debe usar throwuno Exceptionque describa el error con mayor precisión. También hay JUnit Assertpero eso es para pruebas.
Cambunctious
Estaba pensando más bien, por ejemplo, para una función de raíz cuadrada Objects.requireTrue(x >= 0.0);, o para algún hash,Objects.requireEquals(hash.length == 256)
user949300
¿Qué @Nonnull tengo que usar? javax.validation.constraints.NotNull?
Aguid
Me gustaría utilizar las anotaciones Eclipse JDT , porque están hechos por chicos :) inteligente. Documentación: help.eclipse.org/neon/… - También puede configurar IntelliJ para usarlos.
koppor
2

Siempre prefiero lanzar IllegalArgumentException sobre aserciones.

Las afirmaciones se usan principalmente en JUnit u otras herramientas de prueba, para verificar / afirmar los resultados de la prueba. Por lo tanto, podría dar una falsa impresión a otros desarrolladores de que su método es un método de prueba.

También tiene sentido lanzar IllegalArgumentException cuando un método ha pasado un argumento ilegal o inapropiado . Esto es más coherente con la convención de manejo de excepciones seguida por los desarrolladores de Java.

rai.skumar
fuente
55
Las afirmaciones ocurrieron alrededor de 40 años antes de JUnit. Pregúntele a un programador de C sobre macros ASSERT.
JBRWilkinson
3
esta pregunta no se trata de c; Se trata de Java. Entonces he respondido en contexto de Java.
rai.skumar
No confunda la afirmación (la palabra clave reservada) frente a Assert (la clase JUnit), ambas se usan para realizar una verificación de una variable, pero más allá de eso son dos cosas muy diferentes y se comportan de manera muy diferente.
Newtopian
1

OMI, el segundo es un poco mejor porque trae más información y podría ampliarse aún más (por ejemplo, al extender la clase de excepción) para ser aún más informativo, además no utiliza una comparación negativa que es más fácil de entender.

0lukasz0
fuente
1

No uso mucho aserts, pero un enfoque común con Lombock @NonNull: https://projectlombok.org/features/NonNull

Implementación de Lombok: import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Versión de Java:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

Lombok es una biblioteca realmente bonita que uso en todas partes

kensai
fuente