2010-05-03 20 views
25

Mentre spuntavo tra le domande, ho scoperto di recente la parola chiave assert in Java. All'inizio ero eccitato. Qualcosa di utile che non sapevo già! Un modo più efficiente per me per verificare la validità dei parametri di input! Yay learning!Java assert rotto?

Ma poi ho preso uno sguardo più attento, e il mio entusiasmo non era tanto "temperato" come "spenta-out completamente" da un semplice fatto:. È possibile attivare le asserzioni off *

Questo suona come un incubo . Se sto affermando che non voglio che il codice continui se l'input listOfStuff è null, perché mai vorrei ignorare questa asserzione? Sembra che se eseguo il debug di un codice di produzione e sospetti che listOfStuff sia stato erroneamente passato a null ma non si veda alcuna evidenza di log di quell'asserzione che viene attivata, non posso fidarmi che in realtà sia stato inviato uno listOfStuff valido valore; Devo anche spiegare la possibilità che le asserzioni possano essere state completamente disattivate.

E questo presuppone che io sia quello che esegue il debug del codice. Qualcuno che non ha familiarità con le asserzioni potrebbe vederlo e supporre (abbastanza ragionevolmente) che se il messaggio di asserzione non appare nel log, il problema potrebbe non essere il listOfStuff. Se il tuo primo incontro con assert era in libertà, ti verrebbe in mente che potrebbe essere disattivato completamente? Non è che ci sia un'opzione a riga di comando che ti consente di disabilitare i blocchi try/catch, dopo tutto.

Tutto ciò mi porta alla mia domanda (e questo è una domanda, non è una scusa per un rant lo prometto!!):

Che cosa mi manca?

C'è qualche sfumatura che rende l'implementazione di Java di assert molto più utile di quella a cui sto dando credito? La capacità di abilitarlo/disabilitarlo dalla riga di comando è davvero incredibilmente valido in alcuni contesti? Sono in qualche modo misconceptualizing esso in qualche modo quando immagino di utilizzarlo nel codice di produzione al posto di dichiarazioni come if (listOfStuff == null) barf();?

Mi sento come se qui non ci fosse qualcosa di importante.

* Ok, tecnicamente parlando, in realtà sono disattivati ​​per impostazione predefinita; devi fare di tutto per accenderli. Ma comunque, puoi batterli completamente.


Modifica: Illuminismo richiesto, illuminazione ricevuta.

L'idea che lo strumento assert sia innanzitutto uno strumento di debug ha un lungo, lungo percorso per renderlo comprensibile per me.

Sono ancora in disaccordo con l'idea che i controlli di input per i metodi privati ​​non banali dovrebbero essere disabilitati in un ambiente di produzione perché lo sviluppatore ritiene che gli input errati siano impossibili. Nella mia esperienza, il codice di produzione maturo è una cosa folle e tentacolare, sviluppata nel corso degli anni da persone con vari gradi di abilità mirate a soddisfare rapidamente le esigenze di vari gradi di sanità mentale. E anche se l'input errato è davvero impossibile, un pezzo di codifica di manutenzione trascurata tra sei mesi può cambiarlo. The link gustafc provided (grazie) include questo come esempio:

assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;

disabilitazione di un semplice controllo ad esempio nella produzione mi sembra stupidamente ottimista. Tuttavia, questa è una differenza nella filosofia di codifica, non una caratteristica rotta.

Inoltre, posso sicuramente vedere il valore di qualcosa di simile:

assert reallyExpensiveSanityCheck(someObject) : someObject;

I miei ringraziamenti a tutti coloro che hanno trovato il tempo per aiutarmi a capire questa funzione; è molto apprezzato

+10

Questo assomiglia molto a un rant, e non come una domanda affatto. –

+3

Hai mai usato ASSERT in C o C++? Hai mai desiderato di poterli accendere o spegnere allo scatto di un dito? –

+1

@Joachim: ho capito, ma seriamente, sono qui in cerca di illuminazione. Questa funzione mi sembra davvero infranta, ma è come dire "Non vedo cosa c'è che non va nel mio codice, quindi il compilatore deve essere rotto!", Solo a un livello più alto. Mi sento davvero come se mi mancasse qualcosa. Se mi puoi suggerire un modo per esprimere PERCHÉ il lungometraggio mi sembra spezzato che non ti colpisce come uno sfogo, ti sto ascoltando. – BlairHippo

risposta

21

assert è un utile elemento di Design per contratto. In tale contesto, le asserzioni possono essere utilizzate in:

  • Controlli di precondizione.
  • Controlli postcondizionati.
  • Controlli dei risultati intermedi.
  • Controlli invarianti di classe.

asserzioni possono essere costosi per valutare (si pensi, ad esempio, la classe invariante, che deve tenere prima e dopo di chiamare qualsiasi metodo pubblico della classe). Le asserzioni sono in genere ricercate solo nelle build di debug e per scopi di test; asserisci cose che non possono accadere - cose che sono sinonimi di avere un bug. Le asserzioni verificano il codice contro la propria semantica.

Le asserzioni non sono un meccanismo di convalida dell'input. Quando l'input può essere realmente corretto o sbagliato nell'ambiente di produzione, ad esempio per i livelli input-output, utilizzare altri metodi, come eccezioni o vecchi controlli condizionali.

0

asserzioni devono indicare un problema nel codice che può essere recuperata, o come un aiuto nel debugging. Dovresti usare un meccanismo più distruttivo per errori più gravi, come fermare il programma.

Possono anche essere utilizzati per la cattura di un errore irreversibile prima che l'applicazione non riesce più avanti in scenari di debug e test per aiutare a restringere un problema. Parte della ragione di ciò è che il controllo dell'integrità non riduce le prestazioni del codice ben collaudato in produzione.

Inoltre, in alcuni casi, come ad esempio una perdita di risorse, la situazione potrebbe non essere auspicabile, ma le conseguenze di fermare il programma sono peggiori rispetto alle conseguenze di proseguire.

+0

Non sono d'accordo che le asserzioni dovrebbero essere usate per gli errori recuperabili che ci si aspetta che si verifichino nella produzione. Questo è generalmente il motivo per cui le eccezioni controllate sono valide. In alcuni casi, tuttavia, né le eccezioni né le affermazioni sono appropriate. –

4

Penso che sia il modo in cui asserire l'utilizzo è interpretato e immaginato.

Se davvero si vuole aggiungere l'assegno nel codice produzione effettiva, perché non usare se direttamente o qualsiasi altra istruzione condizionale?

Coloro essere già presente nel linguaggio, l'idea di affermare era solo per avere aggiungere affermazioni dello sviluppatore solo se in realtà non si aspettano che questa condizione accada mai.

Ad esempio, controllando un oggetto come null, diciamo che uno sviluppatore ha scritto un metodo privato e lo ha chiamato da due punti (questo non è un esempio ideale ma può funzionare per metodi privati) nella classe in cui sa che passa un non null oggetto, invece di aggiungere controlli non necessari se da oggi si sa che non c'è modo l'oggetto sarebbe nullo Ma se qualcuno domani chiama questo metodo con argomento nullo, nel test dell'unità sviluppatore questo può essere catturato a causa della presenza di asserzione e in codice finale non hai ancora bisogno di un controllo se.

+0

Ciò conferisce maggiore fiducia alla completezza (o all'esistenza!) Dei test unitari di quanto io ritenga saggio, ma tuttavia ne vedo la logica. :-) Grazie. – BlairHippo

4

Ogni lingua che abbia mai visto con le affermazioni ha la capacità di chiuderle. Quando scrivi un'affermazione, dovresti pensare "questo è sciocco, non c'è modo nell'universo che questo possa mai essere falso" - se pensi che possa essere falso, dovrebbe essere un controllo degli errori. L'asserzione serve solo ad aiutarti durante lo sviluppo se qualcosa va terribilmente male; quando costruisci il codice per la produzione li disabiliti per risparmiare tempo ed evita (si spera) controlli superflui

+1

Penso che sia una differenza nella filosofia che mi ha fatto inciampare. Per me, la sanità mentale che controlla i parametri di input non è un commento sull'opportunità o meno di sbagliare; sta dicendo che questo metodo ha bisogno di loro per non sbagliare. Certo, potrei pensare che gli input negativi siano impossibili, ma cosa ne so? La base del codice è enorme, la mia comprensione di ciò è incompleta, e ad un certo punto in futuro qualche idiota (come, sai, io) potrebbe rendere nuovamente possibili gli input sbagliati. Ma detto questo, vedo il valore di essere in grado di attivare o disattivare i controlli costosi. – BlairHippo

+0

"dovrebbe essere un controllo degli errori": vuoi dire, un errore nei dati di input del programma, piuttosto che un controllo di un errore nel programma stesso? – Raedwald

19

Le asserzioni di Java non sono realmente fatte per la convalida dell'argomento - è specifically stated that assertions are not to be used instead of dear old IllegalArgumentException (e nemmeno le modalità di utilizzo in C-ish le lingue). Sono più lì per la convalida interna, per farti assumere un'ipotesi sul codice che non è ovvio dal guardarlo.

Per quanto riguarda disattivandoli, lo si fa in C (++), troppo, solo che se qualcuno ce l'ha una build assert-meno, non hanno modo di accenderlo. In Java, devi solo riavviare l'app con i parametri della VM appropriati.

+1

Non è proprio quello che dice il link. Dice che non devono essere utilizzati per la convalida degli argomenti nei metodi PUBLIC - fornisce esplicitamente un esempio di utilizzo per la convalida degli argomenti in quelli PRIVATI. E per fare ipotesi sul codice che non è ovvio, non è tipicamente il lavoro dei commenti? Non sapevo che su C/C++, però; Sicuramente vedo come accenderli tramite l'interruttore della riga di comando è più attraente che eseguire una ricompilazione completa. – BlairHippo

+0

Sì, è possibile utilizzare asserzioni per determinati metodi privati. Ma non dovrebbero esserci chiamate non valide a quei metodi privati ​​in produzione (se non è possibile garantire ciò, utilizzare le eccezioni o un altro meccanismo). Quella pagina dice esplicitamente che gli asserimenti dovrebbero essere usati per documentare ipotesi, piuttosto che commenti. Vedi l'esempio di 'i% 3 == 2'. –

1

Questo non risponde direttamente alla tua domanda circa assert, ma mi consiglia di controllare la classe Preconditions in guava/google-collections.Esso consente di scrivere roba bella come questo (usando le importazioni statiche):

// throw NPE if listOfStuff is null 
this.listOfStuff = checkNotNull(listOfStuff); 

// same, but the NPE will have "listOfStuff" as its message 
this.listOfStuff = checkNotNull(listOfStuff, "listOfStuff"); 

Sembra che qualcosa di simile potrebbe essere ciò che si vuole (e non può essere disattivata).

1

Le asserzioni non sono visibili all'utente finale. Sono per il programmatore, quindi puoi assicurarti che il codice stia facendo la cosa giusta mentre è in fase di sviluppo. Una volta terminato il test, le affermazioni vengono solitamente disattivate per motivi di prestazioni.

Se stai anticipando che qualcosa di brutto sta per accadere in produzione, come listOfStuff è nullo, allora il tuo codice non è stato testato abbastanza, o non stai disinfettando il tuo input prima di lasciare che il tuo codice abbia . Ad ogni modo, un "if (cose cattive) {lancia un'eccezione}" sarebbe meglio. Le asserzioni sono per test/tempo di sviluppo, non per la produzione.

4

Le asserzioni sono intese a garantire che le cose che si è sicuri che il codice soddisfi siano realmente soddisfatte. È un aiuto nel debugging, nella fase di sviluppo del prodotto, ed è solitamente omesso quando il codice viene rilasciato.

Cosa mi manca?

Non si stanno utilizzando le asserzioni nel modo in cui sono state pensate per essere utilizzate. Hai detto "controlla la validità dei parametri di input" - questo è esattamente il tipo di cose che fai non vuoi verificare con le asserzioni.

L'idea è che se un'asserzione fallisce, hai 100% un bug nel tuo codice. Le asserzioni sono spesso utilizzate per identificare il bug prima di quanto sarebbe emerso altrimenti.

+1

"100% hai un bug nel tuo codice": nel codice stesso, o nel codice del controllo di asserzione. – Raedwald

3

Questo suona bene. Le asserzioni sono solo uno strumento utile per il debug del codice: non dovrebbero essere attivate in ogni momento, specialmente nel codice di produzione.

Ad esempio, in C o C++, le asserzioni sono disabilitate nelle versioni di rilascio.

+1

... e in Java per impostazione predefinita. – DJClayworth

3

Le asserzioni sono davvero uno strumento di documentazione eccezionale e conciso per un manutentore del codice.

Ad esempio posso scrivere:

foo dovrebbe essere non nullo e maggiore di 0

o messo questo nel corpo del programma:

assert foo != null; 
assert foo.value > 0; 

Sono estremamente utili per documentare metodi privati ​​/ pacchetti privati ​​per esprimere invarianti di programmatori originali.

Per il bonus aggiuntivo, quando il sottosistema inizia a comportarsi in modo instabile, è possibile attivare gli asserti e aggiungere immediatamente ulteriore convalida.

2

Se gli asserti non possono essere disattivati, perché dovrebbero esistere.

Se si vuole Performa controllo di validità su un ingresso, si può facilmente scrivere

if (foobar<=0) throw new BadFoobarException(); 

o apparire una finestra di messaggio o quant'altro sia nel contesto.

L'intero punto di affermazione è che sono qualcosa che può essere attivato per il debug e disattivato per la produzione.

1

Utilizzare un assert se si è disposti a pagare $ 1 al proprio utente finale ogni volta che l'asserzione fallisce.

Un errore di asserzione dovrebbe essere un'indicazione di un errore di progettazione nel programma.

Un'affermazione afferma che il programma è stato progettato in modo tale da garantire e garantire che il predicato specificato sia sempre valido.

Un'asserzione è utile per i lettori del mio codice, dal momento che vedono che (1) sono disposto a mettere un po 'di soldi su quella proprietà; e (2) nelle precedenti esecuzioni e nei casi di test, la proprietà era effettivamente valida.

La mia scommessa presuppone che il client del mio codice si attiene alle regole e aderisce al contratto che lui e io abbiamo concordato. Questo contratto può essere tollerante (tutti i valori di input consentiti e verificati per la validità) o esigente (il cliente ed io siamo d'accordo che non fornirà mai determinati valori di input [descritti come precondizioni] e che non vuole che controlli questi valori ancora e ancora). Se il cliente si attiene alle regole e comunque le mie asserzioni falliscono, il cliente ha diritto ad un risarcimento.