Dovresti affermare che non è nullo con l'affermazione assert nel codice di produzione? [chiuso]


32

Ho visto questa domanda ma ho qualche altra domanda sull'uso della assertparola chiave. Stavo discutendo con alcuni altri programmatori sull'uso assert. Per questo caso d'uso, esisteva un metodo che può restituire null se vengono soddisfatti determinati prerequisiti. Il codice che ho scritto chiama il metodo, quindi afferma che non restituisce null e continua a utilizzare l'oggetto restituito.

Esempio:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Ora immagina di usarlo in questo modo:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

Mi è stato detto che dovrei rimuovere il assert, che non dovrebbero mai essere usati nel codice di produzione, da usare solo nei test. È vero?


19
Per impostazione predefinita, le asserzioni sono disabilitate. Devi abilitarli esplicitamente in fase di esecuzione tramite -eao equivalente.
jarmod

Domanda correlata sull'ingegneria del software : softwareengineering.stackexchange.com/q/137158/187318 (anche se è piuttosto vecchia, quindi la risposta accettata lì raccomanda ancora una libreria esterna, che non è più necessaria dall'introduzione di Objects.requireNonNullJava 8).
Hulk

Il titolo di questa domanda è eccessivamente ampio, ma la domanda dichiarata nel corpo è molto più ristretta e per le risposte è completamente gestita da funzioni di supporto.
smci

Per essere chiari, stai chiedendo se il codice client deve implementare controlli / assert non nulli sugli oggetti. (È diverso dal chiedere se il codice della libreria stesso debba garantire che gli oggetti non possano essere nulli o affermare che lì).
smci

Risposte:


19

Usa Objects.requireNonNull(Object)per quello.

Verifica che il riferimento all'oggetto specificato non sia nullo. Questo metodo è progettato principalmente per eseguire la convalida dei parametri in metodi e costruttori, [...]

Nel tuo caso questo sarebbe:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Questa funzione è realizzata per gli scopi che hai citato, ovvero contrassegnare esplicitamente ciò che non deve essere nullo; anche in produzione. Il grande vantaggio è che ti assicuri di trovare valori nulli proprio dove non dovrebbero verificarsi in primo luogo. Avrai meno problemi di debug causati da valori null che sono stati passati da qualche parte dove non dovrebbero essere.

Un altro vantaggio è la flessibilità aggiuntiva rispetto ai controlli null rispetto a assert. Mentre assertè una parola chiave per controllare un valore booleano, Objects.requireNonNull(Object)è una funzione e quindi può essere incorporata nel codice molto più flessibile e leggibile. Per esempio:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Tieni presente che Objects.requireNonNull(Object)è esclusivamente per il controllo null doveassert è generalizzato.assertha scopi leggermente diversi al riguardo, vale a dire principalmente i test. Deve essere abilitato, quindi è possibile abilitarlo per i test e disabilitarlo per la produzione. Comunque, non usarlo per il codice di produzione. Potrebbe rallentare l'applicazione con convalide inutili e complicate destinate al test. Usalo per separare i controlli di solo test dai controlli che sono destinati anche alla produzione.

Controlla la documentazione ufficiale per i dettagli assert.


14
Chi e quando dichiarato afferma di essere legato? Eventuali collegamenti / riferimenti? Non riesco a immaginare nessuno sviluppatore sano che definisca "legacy" lo strumento che consente convalide a zero footprint che possono essere facilmente abilitate nei test e disabilitate in produzione.
Dmitry Pisklov il

Dato che puoi decidere in fase di esecuzione se assert funziona, ma non puoi determinare in fase di esecuzione se l'NPE viene lanciato da requestNonNull, cosa intendi con 'hai più controllo con esso che con assert'?
Pete Kirkham il

@DmitryPisklov Hai ragione, l'ho modificato. È un ottimo strumento per i test.
Akuzminykh

2
@PeteKirkham Hai ragione, controllo era la parola sbagliata per quello. Stavo più parlando della flessibilità nella sintassi. Inoltre: ecco i motivi per cui si desidera che il codice generi NPE.
Akuzminykh

1
"non usarlo per il codice di produzione. Potrebbe rallentare l'applicazione" Potrebbe, ma potrebbe valerne la pena. Java ha controlli di runtime integrati per garantire che non venga mai superato un array. Questi sono abilitati anche su distribuzioni di produzione, nonostante la possibile riduzione delle prestazioni. Non ci viene nemmeno data una scelta al riguardo. Non è sempre sbagliato avere controlli di runtime nel codice di produzione.
Max Barraclough,

22

La cosa più importante da ricordare sulle asserzioni è che possono essere disabilitate, quindi non dare mai per scontato che vengano eseguite.

Per compatibilità con le versioni precedenti, JVM disabilita la convalida dell'asserzione per impostazione predefinita. Devono essere esplicitamente abilitati utilizzando l'argomento della riga di comando -enableassertions o la sua scorciatoia -ea:

java -ea com.whatever.assertion.Assertion

Quindi, non è una buona pratica fare affidamento su di essi.

Poiché le asserzioni non sono abilitate per impostazione predefinita, non si può mai presumere che verranno eseguite quando utilizzate nel codice. Quindi dovresti sempre controllare i valori null e gli Optionals vuoti, evitare di usare asserzioni per controllare gli input in un metodo pubblico e invece usare un'eccezione non selezionata ... In generale fai tutti i controlli come se l'asserzione non fosse lì.


Ovviamente è una cattiva pratica fare affidamento su di essi, ma è una cattiva pratica usarla in generale?
Big_Bad_E

3
@Big_Bad_E Le asserzioni sono uno strumento di debug che esiste per far fallire un programma il prima possibile e il più rumorosamente possibile. È quindi compito della suite di test assicurarsi che non ci sia modo di fallire un programma nonostante le affermazioni . L'idea è che, una volta che la suite di test (ha una copertura del 100%, no?!?) Viene eseguita senza errori, si salva per rimuovere le asserzioni in quanto non possono comunque attivarsi. Sicuramente c'è un po 'di idealismo in questo ragionamento, ma questa è l'idea.
cmaster - ripristina monica il

11

Sicuramente quello che ti viene detto è una palese menzogna. Ecco perché.

Le asserzioni sono disabilitate per impostazione predefinita se si esegue jvm autonomo. Quando sono disabilitati, hanno un footprint pari a zero, quindi non influiranno sull'applicazione di produzione. Tuttavia, sono probabilmente i tuoi migliori amici quando sviluppano e testano il tuo codice e la maggior parte dei test runner abilitano asserzioni (lo fa JUnit), quindi il tuo codice di asserzione viene eseguito quando esegui i test unitari, aiutandoti a rilevare eventuali potenziali bug in precedenza (ad es. è possibile aggiungere assert per alcuni controlli dei limiti della logica aziendale e ciò aiuterà a rilevare del codice che utilizza valori inappropriati).

Detto questo, come suggerisce l'altra risposta, proprio per quel motivo (non sono sempre abilitati) non puoi fare affidamento su asserzioni per fare alcuni controlli vitali, o (soprattutto!) Mantenere qualsiasi stato.

Per un esempio interessante di come usare gli assert, dai un'occhiata qui - alla fine del file c'è un metodo singleThreadedAccess()che viene chiamato dall'istruzione assert sulla linea 201 ed è lì per catturare qualsiasi potenziale accesso multithread nei test.


4

Le altre risposte già lo trattano abbastanza bene, ma ci sono altre opzioni.

Ad esempio, Spring ha un metodo statico:

org.springframework.util.Assert.notNull(obj)

Ci sono anche altre librerie con i loro Assert.something()metodi. È anche abbastanza semplice scrivere il tuo.

Tuttavia, tieni presente quali eccezioni vengono generate se si tratta di un servizio Web. Il metodo precedente menzionato, ad esempio, genera unIllegalArgumentException che per impostazione predefinita in Spring restituisce un 500.

Nel caso di un servizio Web, questo spesso non è un errore interno del server e non dovrebbe essere un 500, ma piuttosto un 400, che è una richiesta errata.


1
Se il metodo "Assert" non interrompe immediatamente il processo, generando invece una sorta di eccezione, non è un'asserzione. Il punto assertè: ottenere un crash immediato con un file core prodotto in modo da poter fare un post-mortem con il tuo debugger preferito nel punto in cui è stata violata la condizione.
cmaster - ripristina monica il

Le asserzioni non bloccano immediatamente un processo. Emettono un errore, che può essere catturato. Le eccezioni non gestite causeranno l'arresto anomalo dei test e errori. Puoi argomentare la semantica se vuoi. Se lo desideri, scrivi un'asserzione personalizzata che genera un errore anziché un'eccezione. Il fatto è, nella stragrande maggioranza dei casi, che l'azione appropriata sia quella di lanciare un'eccezione piuttosto che un errore.
Christopher Schneider,

Ah, Java definisce davvero assertdi lanciare. Interessante. La versione C / C ++ non fa una cosa del genere. Solleva immediatamente un segnale che a) uccide il processo e b) crea un dump core. Lo fa per un motivo: è estremamente semplice eseguire il debug di un errore di asserzione del genere poiché sono ancora disponibili tutte le informazioni sullo stack di chiamate. La definizione assertdi lanciare invece un'eccezione, che può quindi essere catturata (non) intenzionalmente a livello di programmazione, sconfigge lo scopo, imho.
cmaster - ripristina monica il

Proprio come ci sono alcune (o molte) cose strane in C e C ++, ce ne sono alcune in Java. Uno di questi è Throwable. Possono sempre essere catturati. Che sia una cosa buona o no, non lo so. Immagino che dipenda. Molti programmi Java sono servizi Web e non sarebbe auspicabile che si arrestasse in modo anomalo per quasi tutti i motivi, quindi praticamente tutto viene intercettato e registrato. Una delle grandi cose è la traccia dello stack, e di solito è sufficiente per diagnosticare da sola il motivo di un'eccezione o errore.
Christopher Schneider,

3

L'uso asserisce liberamente ogni volta che lo fa aiuta a rilevare errori di programmazione, ad esempio bug.

Non usare assert per catturare qualcosa che potrebbe accadere logicamente, ad esempio input formattato male. Utilizzare assert solo quando l'errore è irrecuperabile.

Non inserire alcuna logica di produzione nel codice che viene eseguito quando viene verificata l'asserzione. Se il tuo software è ben scritto, questo è banalmente vero, ma se non lo è, potresti avere effetti collaterali sottili e diversi comportamenti generali con affermazioni abilitate e disabilitate.

Se la tua azienda ha "codice di prova" e "codice di produzione" che fanno la stessa cosa ma come basi di codice diverse (o diverse fasi di modifica), esci da lì e non tornare mai più. Cercare di risolvere quel livello di incompetenza è probabilmente una perdita di tempo. Se la tua azienda non inserisce dichiarazioni di asserzione al di fuori del codice dei test, informa gentilmente che le asserzioni sono disabilitate nella build di produzione e che, in caso contrario, correggere l'errore è ora la tua priorità.

Il valore delle asserzioni deve essere esattamente utilizzato nella logica di business e non solo nella suite di test. Questo rende facile sfornare molti test di alto livello che non devono testare esplicitamente molte cose per passare attraverso grossi blocchi del codice e innescare tutte queste affermazioni. In alcuni dei miei progetti i test tipici non hanno nemmeno affermato nulla, hanno semplicemente ordinato un calcolo basato su input specifici e questo ha causato il controllo di centinaia di asserzioni e il rilevamento di problemi anche in minuscoli logici in profondità.


2

Puoi usare assert in qualsiasi momento. Il dibattito arriva è quando usare. Ad esempio nella guida :

  • Non utilizzare asserzioni per il controllo degli argomenti nei metodi pubblici.
  • Non utilizzare le asserzioni per eseguire qualsiasi lavoro richiesto dall'applicazione per il corretto funzionamento.

4
Buone regole. Ma la risposta sarebbe migliore con alcune ragioni aggiunte.
cmaster - ripristina monica il
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.