2015-08-23 19 views
31

Provengo da uno sfondo C++ e attualmente sto imparando Java. Una domanda è sorto quando ho provato a utilizzare alcune librerie di terze parti. Come determinare se la chiamata a un metodo che utilizza un riferimento oggetto come parametro modifica l'oggetto? In C++ questo è chiaro grazie all'uso della parola chiave const. Se la firma del metodo è:Come determinare se un metodo Java modifica un oggetto passato come parametro

void foo(Boo& boo); 

So che l'oggetto si fa riferimento potrebbe essere modificato, mentre se la firma del metodo è:

void foo(const Boo& boo); 

Le garanzie compilatore che l'oggetto di riferimento non viene modificato.

Non ho visto qualcosa di analogo in Java, in quanto solo il riferimento stesso può essere dichiarato definitivo, non l'oggetto di riferimento e un argomento finale non ha molto senso in primo luogo poiché viene comunque passato per valore . Perciò, quando vedo un metodo come:

void foo(Boo boo) {...} 

Come faccio a determinare se l'oggetto a cui fa riferimento boo viene modificato all'interno del corpo della funzione (magari usando le annotazioni)? Se non c'è modo di sapere, c'è qualche convenzione ampiamente utilizzata o alcune buone pratiche per evitare confusione e bug?

+4

Leggi i documenti. (Si potrebbe considerare questo un punto debole in Java rispetto a C++, ma non ci manca davvero, non più di quanto non abbiate controllato le eccezioni quando si lavora in C++.) – user2357112

+2

La migliore pratica è quella di evitare la mutazione laddove possibile. Questo vale per tutto, non solo per gli argomenti ai metodi. – usr

+0

@ user2357112 "* più di quanto non abbiate controllato le eccezioni quando si lavora in C++" * L'uso di eccezioni per controllare il flusso del programma è un errore, eccezioni sono (come dice il nome) per ** circostanze eccezionali **. Avere una funzione linguistica il cui unico scopo è quello di essere in grado di recuperare da un errore utilizzando eccezioni in quanto il canale di comunicazione è solo un altro caso di "eccezioni per il controllo del flusso", un errore di progettazione come afferma la regola precedente. Non perdiamo le eccezioni controllate. – Manu343726

risposta

13

Purtroppo non esiste una struttura del genere integrata nella lingua. Una buona pratica difensiva è quella di definire gli oggetti dati che si passano immutabili (vale a dire, senza alcun metodo pubblico che consenta di modificare il loro stato). Se si è veramente interessati a , è possibile copiare/clonare un oggetto prima di passarlo a un metodo di cui non ci si fida, ma di solito si tratta di una precauzione ridondante.

24

come determinare se l'oggetto a cui fa riferimento boo viene modificato all'interno del corpo della funzione (magari utilizzando le annotazioni)?

L'unico modo è leggere il codice sfortunatamente.

Se non c'è modo di sapere, c'è qualche convenzione ampiamente utilizzata o alcune buone pratiche per evitare confusione e bug?

La convenzione comune è passare un oggetto che non può essere modificato, utilizzando un wrapper se necessario. Questo garantisce che la classe non possa modificare l'oggetto.

List<String> readOnly = Collections.unmodifiableList(list); 

Se l'oggetto è Cloneable, è anche possibile utilizzare clone() ma un altro approccio comune è quello di utilizzare una copia.

List<String> readOnly = new ArrayList<>(list); 

Se vi interessa tale comportamento, unit test possono mostrare se un metodo modifica un oggetto o meno. Se si dispone già di test unitari, di solito è necessaria una o più righe per verificarlo.

+1

Come consiglio per qualcuno che esegue la migrazione da C++, sarebbe importante menzionare che non si dovrebbe usare 'clone' in Java, perché [l'idioma è rotto] (http://www.artima.com/intv/bloch13. html). –

+0

@MickMnemonic Dovresti evitare di chiamare clone() se c'è un bug nella sua implementazione. Se l'oggetto non è già Clonabile, renderlo Cloneable è difficile da ottenere correttamente esp per oggetti complessi e dovrebbe essere evitato per oggetti non banali. Ho visto un certo numero di bug che sarebbero stati risolti se "clone()" fosse stato usato in primo luogo, quindi evitarlo e sostituirlo con un altro buggy non è una risposta. –

+2

'clone' è utile solo in pochissimi casi in cui esiste già un'implementazione corretta. Nel nuovo codice, non vi è alcun motivo per esporre 'clone', nemmeno da classi banali; basta fornire invece un costruttore di copia. –

-3

C'è un modo, che lo sviluppatore del metodo deve contrassegnare i parametri come finali, se non sta per modificare il parametro.

Tuttavia, poche persone lo seguono, quindi è difficile saperlo. Tuttavia, un buon programmatore segue questa regola, in particolare scrivendo l'api. Se vuoi scrivere un metodo ed esporlo.Imposta param finale per indicare che l'oggetto passato non verrà modificato.

+7

Fare ciò è una cattiva idea in quanto non fa ciò che sembra fare. Impedisce che il parametro * reference * venga modificato all'interno del metodo, tuttavia poiché questo viene passato per valore, il chiamante non sarà in grado di vedere se lo hai modificato o meno. Non impedirà che l'oggetto 'param' venga modificato. –

+0

Questo compare anche in Javadoc? O attraverso la riflessione? Non sono sicuro di come qualcuno saprebbe se l'hai fatto. – user2357112

+1

@Peter, questa è in realtà una pratica raccomandata, non cattiva in quanto rende chiaro a chiunque che non è necessario riassegnare il riferimento. Hai ragione nel dire che questo non dovrebbe essere confuso con l'immutabilità. Altre discussioni [in questa discussione] (http://stackoverflow.com/questions/316352/why-would-one-mark-local-variables-and-method-parameters-as-final-in-java). –

6

Un'analisi dell'effetto collaterale non è integrata nel linguaggio Java.

È possibile eseguire l'analisi degli effetti collaterali tramite ispezione manuale, ma esistono diversi strumenti per automatizzare il processo.

È possibile utilizzare uno strumento di deduzione (1, 2, 3) per rilevare se il codice ha effetti collaterali su un parametro.

È anche possibile scrivere annotazioni di purezza o di effetto collaterale nel codice e quindi utilizzare uno strumento di verifica/verifica (1, 2) per garantire che il codice sia conforme alle annotazioni scritte.

Tutti gli strumenti sopra collegati presentano limitazioni, ma potrebbero essere utili. Se conosci altri strumenti, menzionali nei commenti.

8

NOTA: questa risposta è una versione più dettagliata di

You can also write purity or side-effect annotations in your code - mernst

Esiste la Checker Framework tra le varie cose che può controllare al momento della compilazione tramite annotazioni è il IJG Immutablity checker. Questo correttore consente di annotare i riferimenti oggetto con @Immutable o @ReadOnly.

Il problema è che spesso dovresti farlo annotate the library. Per facilitare il compito, Checker Framework può aggiungere una parte delle annotazioni a automatically infer; dovrai ancora fare molto da solo

+1

L'annotazione '@ Immutable' impone l'immutabilità dell'oggetto. Il poster originale chiedeva dell'immutabilità del riferimento, che è fornita dall'annotazione '@ ReadOnly'. – mernst

+2

Per quanto riguarda lo stato di [Checker Framework] (http://checkerframework.org/), viene utilizzato da Google su centinaia di progetti ogni giorno. Viene anche utilizzato a Wall Street e da molti altri professionisti e dagli accademici. Non è uno standard ufficiale e non esiste alcun JSR associato: è solo uno strumento utile. – mernst

3

Come determinare se l'oggetto a cui fa riferimento il boo viene modificato all'interno di il corpo della funzione (magari utilizzando le annotazioni)?

Devo d'accordo con le altre risposte che non esiste un modo diretto per determinare che il metodo modificherà l'oggetto o no e sì per assicurarsi che il metodo non può modificare la vostra Object tutti voi che dovete fare è dalla tua parte .

Se non c'è modo di sapere, c'è qualche convenzione ampiamente utilizzata o alcune best practice per evitare confusione e bug?

Qui il metodo nome arriva alla scena. Andando avanti con la convenzione di denominazione del metodo, dobbiamo dare un'occhiata ad alcune dichiarazioni di metodo che ti convincono chiaramente che il tuo Object non sarà affatto cambiato.

Per esempio, si sa che Arrays.copyOf non cambierà la matrice reale, System.out.println(boo) non cambierà la vostra boo

nomi di metodo sono vere e proprie armi di fornire quante più informazioni possibili per l'utente metodo. (Sì, non è sempre possibile ma è una buona pratica da seguire.)

Consideriamo che nel tuo caso che dicono printBoo sarà solo di stampa, copyBoo sarà solo copiare, clearBoo sarà resettare tutti gli attributi, checkAndCreateNewBoo controllerà il tuo booObject e di crearne nuovi, se necessario.

Quindi, in ultima analisi, se possiamo utilizzarli in modo corretto, il chiamante può essere sicuro del fatto che Object rimarrà lo stesso dopo aver chiamato il metodo.

2

Come dicono tutti, preferisce usare oggetti immutabili e anche evitare metodi vuoto

Le finalità disponibili metodi come questo

void foo(Boo boo) {...} 

sono per cambiare lo stato dell'oggetto stesso o modificare l'oggetto passato come parametro

void completOrder(Order order) { ... } 
//or 
void parserTokenEnded(String str) { ... } 
Problemi correlati