È meglio usare assert o IllegalArgumentException per i parametri del metodo richiesti?


88

In Java, che è più raccomandato, e perché? Entrambi i tipi genereranno eccezioni, quindi a questo proposito gestirli è lo stesso. assertè leggermente più corto, ma non sono sicuro di quanto sia importante.

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

vs

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}

Preferisco obj.hashCode()invece un semplice ;-)
Marco

Risposte:


119

ATTENZIONE!

Le asserzioni vengono rimosse in fase di esecuzione a meno che non si specifichi esplicitamente di "abilitare asserzioni" durante la compilazione del codice. Le asserzioni Java non devono essere utilizzate sul codice di produzione e devono essere limitate ai metodi privati (vedere Eccezione vs Asserzione ), poiché si prevede che i metodi privati ​​siano conosciuti e utilizzati solo dagli sviluppatori. Inoltre, assertverrà generato AssertionError che Errornon si estende Exceptione che normalmente indica un errore molto anomalo (come "OutOfMemoryError" da cui è difficile recuperare, non è vero?) Che non ci si aspetta che sia in grado di trattare.

Rimuovi il flag "abilita asserzioni" e verifica con un debugger e vedrai che non passerai alla chiamata di lancio IllegalArgumentException ... poiché questo codice non è stato compilato (di nuovo, quando "ea" viene rimosso)

È meglio usare la seconda costruzione per metodi pubblici / protetti, e se vuoi qualcosa che viene fatto in una riga di codice, c'è almeno un modo che conosco. Io personalmente uso lo Spring Framework 's Assertclasse che ha un paio di metodi per il controllo di argomenti e che tiro 'IllegalArgumentException' in caso di fallimento. Fondamentalmente, quello che fai è:

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

... Che infatti eseguirà esattamente lo stesso codice che hai scritto nel tuo secondo esempio. Ci sono alcuni altri metodi utili come hasText, hasLengthlì dentro.

Non mi piace scrivere più codice del necessario, quindi sono contento quando riduco il numero di righe scritte di 2 (2 righe> 1 riga) :-)


Ah, ho dimenticato che le affermazioni sono state rimosse! Bella risposta. Aspetterò un po 'per vedere se arriva qualcos'altro, poi lo accetterò :)
Daenyth,

2
Si noti che non è presente alcun flag che rimuove le asserzioni al momento della compilazione (sebbene possano essere rimosse tramite la computazione condizionale ). Le asserzioni sono disabilitate in fase di esecuzione di default (penso che la JVM le tratti come NOOP), ma possono essere abilitate tramite java -eae a livello di programmazione. @Jalayn Penso che avere affermazioni nel codice di produzione sia perfettamente valido, in quanto sono utili per il debug sul campo
Justin Muller,

@Jalayn, -1. Il compilatore non rimuove il codice di asserzione. Anche se non verranno eseguiti a meno che non si esegua cmd java -ea.
Pacerier,

5
non è necessario il framework Spring quando è possibile utilizzarloObjects.requreNonNull
1717

46

Devi usare un'eccezione. L'uso di un'asserzione sarebbe un uso improprio della funzione.

Le eccezioni non selezionate sono progettate per rilevare errori di programmazione degli utenti della tua libreria, mentre le asserzioni sono progettate per rilevare errori nella tua logica. Questi sono problemi separati che non devono essere mescolati.

Ad esempio, un'affermazione

assert myConnection.isConnected();

significa "So che ogni percorso di codice che porta a questa affermazione assicura che myConnectionsia collegato; se il codice sopra non è riuscito a ottenere una connessione valida, avrebbe dovuto generare un'eccezione o tornare prima di raggiungere questo punto."

D'altra parte, un assegno

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

significa che "Chiamare la mia libreria senza stabilire una connessione è un errore di programmazione".


1
Questa informazione è davvero utile, ma sto accettando Jalayn poiché sottolinea come potrei potenzialmente introdurre un bug con il metodo assert.
Daenyth,

4
punto eccellente "Le eccezioni non selezionate sono progettate per rilevare errori di programmazione degli utenti della tua libreria, mentre le asserzioni sono progettate per rilevare errori nella tua logica. Questi sono problemi separati che non devono essere mescolati."
Asim Ghaffar,

Vero, ma ciò presuppone che OP stia scrivendo una libreria. Se il loro codice è solo per uso interno, un'affermazione è accettabile.
user949300

11

Se si sta scrivendo una funzione che non consente nullcome valore di parametro valido, è necessario aggiungere l' @Nonnullannotazione alla firma e utilizzare Objects.requireNonNullper verificare se l'argomento è nulle lanciare un NullPointerExceptionse lo è. L' @Nonnullannotazione è per la documentazione e in alcuni casi fornirà avvisi utili al momento della compilazione. Non impedisce il nullpassaggio in fase di esecuzione.

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

    // do stuff...
}

Aggiornamento: l' @Nonnullannotazione non fa parte della libreria standard Java. Esistono invece numerosi standard concorrenti delle librerie di terze parti (vedi Quale annotazione @NotNull Java dovrei usare? ). Ciò non significa che sia una cattiva idea usarlo, solo che non è standard.


Grazie per aver sottolineato Objects.requireNonNull(), che era nuovo per me. Sai se esiste un metodo simile "requireTrue ()" o "requireEqual ()" ovunque? Nulla contro Spring's Assert, ma non tutti lo usano.
user949300,

@ user949300 Objects.requireNonNull()è per la convalida dell'argomento. Se è necessario che un argomento sia trueuguale o uguale a qualcosa, allora l'argomento è inutile. Per i casi di guasto diversi argomenti illegali, si dovrebbe throwuna Exceptionche descrive più accuratamente l'errore. C'è anche JUnit Assertma è per i test.
generoso

Stavo pensando più come, diciamo per una funzione di radice quadrata Objects.requireTrue(x >= 0.0);, o per qualche hash,Objects.requireEquals(hash.length == 256)
user949300

Quale @Nonnull devo usare? javax.validation.constraints.NotNull?
Aguid,

Vorrei usare le annotazioni Eclipse JDT , perché sono fatte da ragazzi intelligenti :). Documentazione: help.eclipse.org/neon/… - È inoltre possibile configurare IntelliJ per utilizzarli.
koppor,

2

Preferisco sempre gettare IllegalArgumentException sulle asserzioni.

Le asserzioni vengono utilizzate principalmente in JUnit o in altri strumenti di test, per controllare / affermare i risultati dei test. Quindi potrebbe dare una falsa impressione ad altri sviluppatori che il tuo metodo sia un metodo di prova.

Inoltre ha senso lanciare IllegalArgumentException quando un metodo ha superato un argomento illegale o inappropriato . Ciò è più coerente con la convenzione di gestione delle eccezioni seguita dagli sviluppatori Java.


5
Le affermazioni erano circa 40 anni prima di JUnit - chiedi a un programmatore C delle macro ASSERT.
JBR Wilkinson,

3
questa domanda non riguarda c; si tratta di Java. Quindi ho risposto nel contesto di Java.
rai.skumar,

Non confondere assert (la parola chiave riservata) vs Assert (la classe JUnit) sono entrambi usati per eseguire un controllo su una variabile ma oltre a ciò sono due cose molto diverse e si comportano in modo molto diverso.
Newtopian,

1

Il secondo IMO è leggermente migliore perché porta più informazioni e potrebbe essere ulteriormente esteso (ad esempio estendendo la classe di eccezioni) per essere ancora più informativo, inoltre non utilizza il confronto negativo che è più facile da capire.


1

Non uso molto gli Aserts, ma un approccio comune con Lombock @NonNull: https://projectlombok.org/features/NonNull

Implementazione di Lombok: import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

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

Versione 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 è una biblioteca davvero carina che uso ovunque

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.