Qual è il punto della classe facoltativa di Guava


89

Recentemente ho letto di questo e ho visto persone che usano questo corso, ma in quasi tutti i casi, anche l'uso nullavrebbe funzionato, se non in modo più intuitivo. Qualcuno può fornire un esempio concreto di dove si Optionalotterrebbe qualcosa che nullnon potrebbe o in un modo molto più pulito? L'unica cosa a cui riesco a pensare è di usarlo con chiavi Mapsche non accettano null, ma anche questo potrebbe essere fatto con una "mappatura" laterale del valore di null. Qualcuno può fornirmi un argomento più convincente? Grazie.


12
Rant casuale: odio quando le persone usano eccessivamente un "pattern" e fanno sembrare il codice così brutto per qualche vantaggio teorico che non esiste ...
RAY

A partire da Java8, non userei più questa classe Guava perché è meno potente di quella JDK. Vedi stackoverflow.com/a/10756992/82609
Sebastien Lorber

Risposte:


155

Membro del team Guava qui.

Probabilmente l'unico più grande svantaggio di nullè che non è ovvio cosa dovrebbe significare in un dato contesto: non ha un nome illustrativo. Non è sempre ovvio che nullsignifichi "nessun valore per questo parametro" - diamine, come valore di ritorno, a volte significa "errore", o anche "successo" (!!), o semplicemente "la risposta corretta è niente". Optionalè spesso il concetto che intendi effettivamente quando fai una variabile nullable, ma non sempre. Quando non lo è, ti consigliamo di scrivere la tua classe, simile a Optionalma con uno schema di denominazione diverso, per chiarire cosa intendi effettivamente.

Ma direi che il più grande vantaggio di Optionalnon è nella leggibilità: il vantaggio è la sua prova di idiota. Ti costringe a pensare attivamente al caso assente se vuoi che il tuo programma venga compilato, dal momento che devi scartare attivamente il Optionalcaso e affrontare quel caso. Null rende estremamente facile dimenticare le cose e, sebbene FindBugs aiuti, non credo che risolva il problema altrettanto bene. Ciò è particolarmente rilevante quando restituisci valori che possono essere o meno "presenti". È molto più probabile che tu (e altri) dimenticherai che other.method(a, b)potrebbe restituire un nullvalore di quanto probabilmente dimenticherai che apotrebbe essere nulldurante l'implementazione other.method. TornandoOptional rende impossibile per i chiamanti dimenticare quel caso, poiché devono scartare l'oggetto da soli.

Per questi motivi, si consiglia di utilizzare Optionalcome tipo restituito per i metodi, ma non necessariamente negli argomenti del metodo.

(Questo è totalmente criptato, a proposito, dalla discussione qui .)


3
+1 per un'ottima spiegazione. Non so se questa sia l'ispirazione, ma aggiungerei un puntatore ai tipi di opzione in SML, OCaml e F #, che hanno molte delle stesse semantiche.
Adam Mihalcin

2
È sempre bello ricevere risposte da qualcuno che è stato direttamente coinvolto. Avrei fatto +1 solo per questo. Farebbe +1 in più per un'ottima risposta. (ma non posso.) Penso che il punto di averlo come valore di ritorno per costringere i consumatori di un metodo non familiare a fare uno sforzo per riconoscere che un "nulla" può essere restituito sia una ragione convincente. Non mi piace però come viene abusato (o addirittura abusato). ad esempio, mi sento davvero a disagio quando vedo persone che inseriscono gli Optionals come valori in Maps. Map returing null per una chiave inesistente / non mappata è un paradigma ben consolidato ... Non c'è bisogno di renderlo più complicato ...
RAY

9
È consolidato che Mapritorni nullse una chiave non è mappata, ma ricorda che se lo fai map.put(key, null), map.containsKey(key)tornerà truema map.get(key)tornerà null. Optionalpuò essere utile per chiarire la distinzione tra il caso "mappato esplicitamente a nullo" e il caso "non presente nella mappa". Ammetto che Optionalpossa essere abusato, ma non sono ancora convinto che il caso che descrivi sia un abuso.
Louis Wasserman

8
Per usare un'analogia antiquata, c'erano queste cose giganti chiamate "elenchi telefonici" :-) e se ti chiedessi di cercare il numero di qualcuno e tu dicessi "non c'è numero per quella persona", direi "cosa fai significa, sono lì con un numero non in elenco o semplicemente non sei riuscito a trovare una voce per loro? " Queste due possibili risposte mappano bene rispettivamente su Optional.absent () e null. Optional.absent () è il "negativo positivo" definitivo.
Kevin Bourrillion

3
@RAY: In un certo senso, Optional<T> è quel valore "trovato, ma non valido". O più precisamente, Optional<T>è un modo per decorare qualsiasi tipo Tcon un valore aggiuntivo "trovato, ma non valido", creando un nuovo tipo combinando due tipi esistenti. Se hai un centinaio di classi, può diventare complicato dover creare un valore separato "trovato, ma non valido" per ciascuna, ma Optional<T>può facilmente funzionare per tutte.
Daniel Pryden

9

Sembra davvero il Maybemodello Monad di Haskell.

Dovresti leggere quanto segue, Wikipedia Monad (programmazione funzionale) :

E leggi From Optional to Monad with Guava on Kerflyn's Blog, which discuss about the Optional of Guava used as a Monad:


Modifica: con Java8, c'è un optional integrato che ha operatori monadici come flatMap. Questo è stato un argomento controverso ma alla fine è stato implementato.

Vedi http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html

public Optional<String> tryFindSimilar(String s)  //...

Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar =       opt.flatMap(this::tryFindSimilar);

L' flatMapoperatore è essenziale per consentire operazioni monadiche e consente di concatenare facilmente le chiamate che restituiscono tutti risultati opzionali.

Pensaci, se usassi l' mapoperatore 5 volte ti ritroveresti con un Optional<Optional<Optional<Optional<Optional<String>>>>>, mentre usando flatMapti darebbeOptional<String>

Dato che Java8 preferirei non usare il Guava's Optional che è meno potente.


6

Un buon motivo per usarlo è che rende i tuoi valori nulli molto significativi. Invece di restituire un null che potrebbe significare molte cose (come errore, fallimento, o vuoto, ecc.) Puoi mettere un "nome" al tuo null. Guarda questo esempio:

definiamo un POJO di base:

class PersonDetails {

String person;
String comments;

public PersonDetails(String person, String comments) {
    this.person = person;
    this.comments = comments;
}

public String getPerson() {
    return person;
}


public String getComments() {
    return comments;
}

}

Ora usiamo questo semplice POJO:

public Optional<PersonDetails> getPersonDetailstWithOptional () {

  PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless,
  lets make the return value more meaningful*/


    if (details == null) {
      //return an absent here, caller can check for absent to signify details are not present
        return Optional.absent();
    } else {
      //else return the details wrapped in a guava 'optional'
        return Optional.of(details);   
    }
}

Ora evitiamo di usare null e facciamo i nostri controlli con Opzionale, quindi è significativo

public void checkUsingOptional () {

    Optional<PersonDetails> details = getPersonDetailstWithOptional();

    /*below condition checks if persons details are present (notice we dont check if person details are null,
    we use something more meaningful. Guava optional forces this with the implementation)*/
    if (details.isPresent()) {

      PersonDetails details = details.get();

        // proceed with further processing
        logger.info(details);

    } else {
        // do nothing
        logger.info("object was null"); 
    }

    assertFalse(details.isPresent());
}

quindi alla fine è un modo per rendere i valori nulli significativi e meno ambigui.


4

Il vantaggio più importante di Opzionale è che aggiunge più dettagli al contratto tra l'implementatore e il chiamante di una funzione. Per questo motivo è utile sia per i parametri che per il tipo restituito.

Se fate la convenzione di avere sempre Optionalper eventuali oggetti nulli aggiungete ulteriori chiarimenti a casi come:

  1. Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)

    Il contratto qui specifica chiaramente che esiste la possibilità che un risultato non venga restituito, ma mostra anche che funzionerà con frome tocome assente.

  2. Optional<Integer> maxPrime(Optional<Integer> from, Integer to)

    Il contratto specifica che il da è facoltativo, quindi un valore assente potrebbe avere un significato speciale come iniziare da 2. Posso aspettarmi che un valore nullo per il toparametro genererà un'eccezione.

Quindi la parte buona dell'utilizzo di Opzionale è che il contratto è diventato sia descrittivo (simile con l' @NotNullannotazione) ma anche formale poiché è necessario scrivere codice .get()per far fronte Optional.


Perché usare un <List> facoltativo quando un elenco vuoto sarebbe più pulito?
RAY

Ho scelto l'esempio sbagliato al ritorno. Con le collezioni puoi fare la convenzione di restituire sempre una collezione vuota invece di un valore nullo. Se viene utilizzata questa convenzione, non è necessario facoltativo per le raccolte. Facoltativo potrebbe essere percepito come una raccolta con zero o un elemento, quindi non è necessario inserirlo in un'altra raccolta.
uva passa

2
a seconda del contesto, potrebbe esserci una differenza significativa tra un elenco con 0 elementi e un elenco assente.
plasma147
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.