Controllare i parametri annotati con @Nonnull per null?


19

Abbiamo iniziato a utilizzare FindBugs e ad annotare i nostri parametri in modo @Nonnullappropriato, e funziona benissimo evidenziare i bug all'inizio del ciclo. Finora abbiamo continuato a controllare questi argomenti per l' nullutilizzo di Guava checkNotNull, ma preferirei verificare nullsolo ai bordi, luoghi in cui il valore può entrare senza essere stato verificato null, ad esempio una richiesta SOAP.

// service layer accessible from outside
public Person createPerson(@CheckForNull String name) {
    return new Person(Preconditions.checkNotNull(name));
}

...

// internal constructor accessed only by the service layer
public Person(@Nonnull String name) {
    this.name = Preconditions.checkNotNull(name);  // remove this check?
}

Capisco che @Nonnullnon blocca i nullvalori stessi.

Tuttavia, dato che FindBugs indicherà ovunque un valore viene trasferito da un campo non contrassegnato a uno contrassegnato @Nonnull, non possiamo dipendere da esso per rilevare questi casi (cosa che fa) senza dover controllare questi valori per nullovunque vengano passati il sistema? Sono ingenuo voler fidarmi dello strumento ed evitare questi controlli dettagliati?

Bottom line: Mentre sembra sicuro rimuovere il secondo nullcontrollo qui sotto, è una cattiva pratica?

Questa domanda è forse troppo simile a Si dovrebbe verificare null se non si aspetta null , ma sto chiedendo specificamente in relazione @Nonnullall'annotazione.

Risposte:


6

Se non ti fidi di FindBug, potrebbe essere un'opzione per usare un'asserzione. In questo modo si evitano i problemi menzionati da User MSalter ma si ha ancora un controllo. Naturalmente, questo approccio potrebbe non essere applicabile se non è possibile adottare un approccio così rapido, vale a dire che è necessario cogliere qualsiasi eccezione.

E per rispondere ulteriormente alla domanda se si tratta di una cattiva pratica. Bene, questo è discutibile ma non lo penserei. Considero questa annotazione FindBug come una specifica leggera che segue il principio di progettazione per contratto.

Nella progettazione per contratto si stabilisce un contratto tra un metodo e il suo chiamante. Il contratto consiste in precondizioni che il chiamante accetta di soddisfare quando chiama il metodo e una postcondizione che a sua volta viene soddisfatta dal metodo dopo l'esecuzione se la sua condizione preliminare è soddisfatta.

Uno dei vantaggi di questo metodo di sviluppo è che puoi evitare un cosiddetto stile di programmazione difensiva (in cui controlli esplicitamente tutti i valori dei parametri non validi ecc.). Puoi invece fare affidamento sul contratto ed evitare controlli ridondanti per aumentare le prestazioni e la leggibilità.

I contratti possono essere utilizzati per il controllo delle asserzioni di runtime che segue il principio fail-first sopra menzionato in base al quale si desidera che il programma fallisca in caso di violazione di un contratto, fornendo un indizio per identificare l'origine dell'errore. (Una precondizione non riuscita significa che il chiamante ha fatto qualcosa di sbagliato, una postcondizione non riuscita indica un errore di implementazione del metodo dato)

Inoltre, i contratti possono essere utilizzati per l'analisi statica, che tenta di trarre conclusioni sul codice sorgente senza eseguirlo effettivamente.

L'annotazione @nonnull può essere vista come una condizione preliminare utilizzata per l'analisi statica dallo strumento FindBugs. Se si preferisce un approccio come il controllo delle asserzioni di runtime, ovvero la prima strategia di insuccesso, è necessario considerare l'utilizzo delle istruzioni di asserzione come Java integrato. O forse per adattarsi a una strategia di specifica più sofisticata utilizzando un linguaggio di specifica dell'interfaccia comportamentale come Java Modeling Language (JML).

JML è un'estensione di Java in cui le specifiche sono incorporate in speciali commenti Java. Un vantaggio di questo approccio è che puoi utilizzare specifiche sofisticate, anche utilizzando un approccio di sviluppo in cui ti affidi solo alle specifiche anziché ai dettagli di implementazione e usi diversi strumenti che fanno uso delle specifiche, come gli strumenti di controllo delle asserzioni di runtime menzionati, generazione automatizzata di unit test o documentazione.

Se condividi il mio punto di vista sul fatto che @nonnull sia un contratto (leggero), sarebbe pratica comune non aggiungere ulteriori controlli perché l'idea originale alla base di questo principio è quella di evitare la programmazione difensiva.


2
Questo è esattamente come sto usando @Nonnull: come una specifica del contratto. Ho rimosso i controlli difensivi dietro il contratto e finora non ho avuto problemi.
David Harkness,

4

Bene, considera perché non scrivi

this.name = Preconditions.checkNotNull(name);  // Might be necessary
that.name = Preconditions.checkNotNull(this.name);  // Who knows?

La programmazione non è magia nera. Le variabili non diventano improvvisamente Null. Se sai che una variabile non è Null e sai che non è cambiata, allora fallo non controllarla.

Se lo controlli, alcuni programmatori in futuro (forse tu) passeranno molto tempo a scoprire perché quel controllo è presente. Il controllo deve essere lì per un motivo, dopo tutto, quindi cosa stai trascurando?


3
Il @Nonnullcontratto prevede che i chiamanti non debbano passare nullal costruttore e FindBugs utilizzi l'analisi statica per individuare le chiamate che potrebbero consentire - in nullparticolare chiamate che passano valori non contrassegnati @Nonnull. Ma un chiamante è libero di passare nulle ignorare l'avvertimento di FindBugs.
David Harkness,

Idealmente, la programmazione non è magia nera. Sfortunatamente, le avvertenze e le sorprese abbondano, specialmente quando si lavora con alcuni dei codici di concorrenza là fuori ♬ ~ ᕕ (ᐛ) ᕗ
Tim Harper,
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.