Perché utilizzare un privilegio opzionale per il controllo nullo della variabile?

18

prendi i due esempi di codice:

if(optional.isPresent()) {
    //do your thing
}

if(variable != null) {
    //do your thing
}

Per quanto posso dire la differenza più ovvia è che l'opzione richiede la creazione di un oggetto aggiuntivo.

Tuttavia, molte persone hanno iniziato ad adottare rapidamente Optional. Qual è il vantaggio dell'utilizzo di opzionali contro un controllo nullo?

    
posta William Dunne 03.02.2016 - 12:08
fonte

9 risposte

17

Optional utilizza il sistema di tipi per eseguire lavori che altrimenti dovresti fare tutti in testa: ricordando se un dato riferimento potrebbe essere null . Questo è buono. È sempre intelligente lasciare che il compilatore gestisca le droghe noiose e riservare il pensiero umano a un lavoro creativo e interessante.

Senza Optional , ogni riferimento nel tuo codice è come una bomba inesplosa. Accedervi può fare qualcosa di utile, altrimenti può terminare il tuo programma con un'eccezione.

Con Opzionale e senza null , ogni accesso a un riferimento normale ha esito positivo e ogni riferimento a un Opzionale ha esito positivo a meno che non sia stato disattivato e non è stato possibile verificarlo. Questa è una grande vittoria nella manutenibilità.

Sfortunatamente, la maggior parte delle lingue che ora offrono Optional non ha abolito null , quindi puoi trarre profitto dal concetto stabilendo una politica rigorosa di "assolutamente no null , mai". Pertanto, Optional in es. Java non è così avvincente come dovrebbe idealmente essere.

    
risposta data 03.02.2016 - 12:22
fonte
13

Un Optional porta una digitazione più strong in operazioni che possono fallire, come le altre risposte hanno coperto, ma questo è lontano dalla cosa più interessante o di valore Optionals da portare alla tabella. Molto più utile è la possibilità di ritardare o evitare il controllo di errori e di comporre facilmente molte operazioni che potrebbero fallire.

Considera se hai avuto la tua variabile optional dal codice di esempio, quindi hai dovuto eseguire due passaggi aggiuntivi che ognuno potrebbe potenzialmente fallire. Se un passaggio lungo il percorso non riesce, si desidera invece restituire un valore predefinito. Usando Optionals correttamente, si finisce con qualcosa di simile:

return optional.flatMap(x -> x.anotherOptionalStep())
               .flatMap(x -> x.yetAnotherOptionalStep())
               .orElse(defaultValue);

Con null avrei dovuto controllare tre volte per null prima di procedere, il che aggiunge un sacco di complessità e problemi di manutenzione al codice. Optionals ha quel controllo integrato nelle funzioni flatMap e orElse .

Nota Non ho chiamato isPresent una volta, che dovresti pensare a un odore di codice quando usi Optionals . Questo non significa necessariamente che mai usi isPresent , solo che dovresti controllare pesantemente qualsiasi codice che faccia, per vedere se c'è un modo migliore. Altrimenti, hai ragione, stai ricevendo un vantaggio sulla sicurezza di tipo marginale rispetto all'utilizzo di null .

Si noti inoltre che non sono così preoccupato di incapsulare tutto questo in un'unica funzione, al fine di proteggere altre parti del mio codice dai puntatori nulli dai risultati intermedi. Se ha più senso avere il mio .orElse(defaultValue) in un'altra funzione, ad esempio, ho molti meno problemi a metterlo lì, ed è molto più facile comporre le operazioni tra le diverse funzioni, se necessario.

    
risposta data 03.02.2016 - 19:50
fonte
10

Evidenzia la possibilità di null come risposta valida, che le persone spesso assumono (a torto oa ragione) non verrà restituita. Evidenziando le poche volte in cui il valore null è valido, è possibile omettere di litterare il codice con un numero enorme di inutili verifiche nulle.

L'impostazione ideale di una variabile su null sarebbe un errore di tempo di compilazione ovunque ma in un opzionale; eliminando eccezioni del puntatore nullo di runtime. Ovviamente la retrocompatibilità preclude questo, purtroppo

    
risposta data 03.02.2016 - 12:15
fonte
4

Lasciatemi fare un esempio:

class UserRepository {
     User findById(long id);
}

È abbastanza chiaro a cosa è destinato questo metodo. Ma cosa succede se il repository non contiene l'utente con un determinato ID? Potrebbe generare un'eccezione o forse restituire null?

Secondo esempio:

class UserRepository {
     Optional<User> findById(long id);
}

Ora, cosa succede qui se non ci sono utenti con un determinato ID? Bene, hai l'istanza Optional.absent () e puoi controllarla con Optional.isPresent (). La firma del metodo con Opzionale è più esplicita sul suo contratto. È immediatamente chiaro come dovrebbe comportarsi il metodo.

Ha anche un vantaggio durante la lettura del codice client:

User user = userRepository.findById(userId).get();

Ho addestrato il mio occhio a vedere .get () come un odore di codice immediato. Significa che il client ignora intenzionalmente il contratto del metodo richiamato. È molto più facile vederlo che senza Opzionale, perché in questo caso non sei immediatamente consapevole di quale sia il contratto del metodo chiamato (se permette o meno il suo ritorno), quindi devi prima controllarlo.

Opzionale è usato con la convenzione che i metodi con tipo di ritorno Opzionale non restituiscono mai null e di solito anche con la convenzione (meno strong) quel metodo senza il tipo di ritorno opzionale non restituisce mai null (ma è meglio annotarlo con @Nonnull, @NotNull o simili).

    
risposta data 03.02.2016 - 12:25
fonte
3

Oltre alle altre risposte, l'altro vantaggio principale di Optionals quando si tratta di scrivere codice pulito è che è possibile utilizzare un'espressione Lambda nel caso in cui il valore sia presente, come mostrato di seguito:

opTr.ifPresent(tr -> transactions.put(tr.getTxid(), tr));
    
risposta data 04.02.2016 - 22:38
fonte
2

Un Optional<T> ti permette di avere "fallimento" o "nessun risultato" come valore di risposta / ritorno valido per i tuoi metodi (pensa, ad es., ad una ricerca nel database). Usare un Optional invece di usare null per indicare il fallimento / nessun risultato ha alcuni vantaggi:

  • Comunica chiaramente che "fallimento" è un'opzione. L'utente del tuo metodo non deve indovinare se è possibile restituire null .
  • Il valore null dovrebbe, almeno a mio parere, non essere usato per indicare qualcosa, in quanto la semantica potrebbe non essere completamente chiara. Dovrebbe essere usato per dire al garbage collector che il precedente oggetto può essere raccolto.
  • La classe Optional fornisce molti buoni metodi per lavorare condizionatamente con i valori (ad esempio ifPresent() ) o definire valori predefiniti in modo trasparente ( orElse() ).

Sfortunatamente, non rimuove completamente la necessità di verifiche null nel codice poiché l'oggetto Optional stesso potrebbe ancora essere null .

    
risposta data 03.02.2016 - 17:43
fonte
2

Considera il seguente metodo:

void dance(Person partner)

Ora diamo un'occhiata al codice di chiamata:

dance(null);

Ciò causa potenzialmente un arresto anomalo della traccia da qualche altra parte, poiché dance non si aspettava un valore nullo.

dance(optionalPerson)

Questo causa un errore di compilazione, perché la danza si aspettava un Person non un Optional<Person>

dance(optionalPerson.get())

Se non avessi una persona, ciò provoca un'eccezione su questa riga, nel punto in cui ho illegittimamente cercato di convertire Optional<Person> in una Persona. La chiave è che, a differenza del passare un null, le tracce eccedono qui, non qualche altra posizione nel programma.

Mettere tutto insieme: l'utilizzo improprio di un codice opzionale produce più facilmente la tracciabilità dei problemi, l'utilizzo improprio di cause Null difficili da rintracciare i problemi.

    
risposta data 03.02.2016 - 18:27
fonte
-1

In Objective-C, tutti i riferimenti agli oggetti sono opzionali. Per questo motivo, codice come te pubblicato è stupido.

if (variable != nil) {
    [variable doThing];
}

Il codice come sopra non è mai visto in Objective-C. Invece, il programmatore vorrebbe solo:

[variable doThing];

Se variable contiene un valore, su di esso verrà chiamato doThing . In caso contrario, nessun danno. Il programma lo tratterà come no-op e continuerà a funzionare.

Questo è il vero vantaggio degli optionals. Non devi sporcare il tuo codice con if (obj != nil) .

    
risposta data 05.02.2016 - 03:40
fonte
-3

if(optional.isPresent()) {

Il problema ovvio qui è che se "facoltativo" in realtà è mancante (vale a dire è nullo) allora il tuo codice esploderà con una NullReferenceException (o simile) che tenta di chiamare qualsiasi metodo su un riferimento a oggetti null!

Potresti voler scrivere un metodo statico, classe Helper, che determina la nullità di qualsiasi tipo di oggetto dato, qualcosa del genere:

function isNull( object o ) 
{
   return ( null == o ); 
}

if ( isNull( optional ) ) ... 

o, forse, più utile:

function isNotNull( object o ) 
{
   return ( null != o ); 
}

if ( isNotNull( optional ) ) ... 

Ma uno di questi è davvero più leggibile / comprensibile / gestibile rispetto all'originale?

if ( null == optional ) ... 
if ( null != optional ) ... 
    
risposta data 03.02.2016 - 14:45
fonte

Leggi altre domande sui tag