Perché utilizzare Opzionale in Java 8+ anziché i tradizionali controlli puntatore null?


110

Di recente ci siamo trasferiti su Java 8. Ora vedo le applicazioni inondate di Optionaloggetti.

Prima di Java 8 (stile 1)

Employee employee = employeeServive.getEmployee();

if(employee!=null){
    System.out.println(employee.getId());
}

Dopo Java 8 (stile 2)

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employee = employeeOptional.get();
    System.out.println(employee.getId());
}

Non vedo alcun valore aggiunto di Optional<Employee> employeeOptional = employeeService.getEmployee();quando il servizio stesso ritorna facoltativo:

Provenendo da uno sfondo Java 6, vedo lo Stile 1 come più chiaro e con meno righe di codice. C'è qualche vero vantaggio che mi manca qui?

Consolidato dalla comprensione di tutte le risposte e ulteriori ricerche nel blog



28
Che schifo! employeeOptional.isPresent()sembra mancare del tutto il punto dei tipi di opzione. Secondo il commento di @ MikePartridge, questo non è raccomandato o idiomatico. Controllare esplicitamente se un tipo di opzione è qualcosa o niente è un no-no. Dovresti semplicemente mapo flatMapsu di loro.
Andres F.

7
Guarda una risposta di overflow dello stackOptional di Brian Goetz , Java Language Architect di Oracle.
Basil Bourque,

6
Questo è ciò che accade quando si spegne il cervello in nome di "migliori pratiche".
immibis,

9
Questo è un uso abbastanza scarso dell'opzionale ma anche quello ha dei vantaggi. Con i null tutto può essere nullo, quindi è necessario controllare costantemente, le opzioni facoltative indicano che è probabile che questa particolare variabile manchi, assicurati di controllarlo
Richard Tingle,

Risposte:


108

Lo stile 2 non ha abbastanza Java 8 per vedere tutti i vantaggi. Non vuoi if ... useaffatto. Vedi gli esempi di Oracle . Seguendo i loro consigli, otteniamo:

Stile 3

// Changed EmployeeServive to return an optional, no more nulls!
Optional<Employee> employee = employeeServive.getEmployee();
employee.ifPresent(e -> System.out.println(e.getId()));

O uno snippet più lungo

Optional<Employee> employee = employeeServive.getEmployee();
// Sometimes an Employee has forgotten to write an up-to-date timesheet
Optional<Timesheet> timesheet = employee.flatMap(Employee::askForCurrentTimesheet); 
// We don't want to do the heavyweight action of creating a new estimate if it will just be discarded
client.bill(timesheet.orElseGet(EstimatedTimesheet::new));

19
infatti vietiamo la maggior parte degli usi di isPresent (catturato dalla revisione del codice)
jk.

10
@jk. anzi, se ci fosse un sovraccarico void ifPresent(Consumer<T>, Runnable), discuterei per un divieto totale di isPresenteget
Caleth, il

10
@Caleth: potresti essere interessato a Java 9 ifPresentOrElse(Consumer<? super T>, Runnable).
wchargin,

9
Trovo uno svantaggio in questo stile Java 8, rispetto a quello Java 6: imbroglia gli strumenti di copertura del codice. Con l'istruzione if, mostra quando hai perso un ramo, non qui.
Thierry,

14
@Aaron "riduce drasticamente la leggibilità del codice". Quale parte riduce esattamente la leggibilità? Sembra più un "L'ho sempre fatto diversamente e non voglio cambiare le mie abitudini" rispetto al ragionamento oggettivo.
Voo

47

Se stai usando Optionalun livello di "compatibilità" tra un'API precedente che potrebbe ancora restituire null, potrebbe essere utile creare l'Opzionale (non vuoto) nell'ultima fase per essere sicuro di avere qualcosa. Ad esempio, dove hai scritto:

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

Opterei per:

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

Il punto è che sai che esiste un servizio per i dipendenti non nullo , quindi puoi concludere questo Optionalcon un Optional.of(). Quindi, quando lo getEmployee()chiedi, potresti avere o meno un dipendente. Quel dipendente può (o possibilmente non avere) un documento d'identità. Quindi, se hai finito con un ID, vuoi stamparlo.

Non è necessario controllare esplicitamente eventuali null, presenza, ecc., In questo codice.


4
Qual è il vantaggio dell'utilizzo di (...).ifPresent(aMethod)over if((...).isPresent()) { aMethod(...); }? Sembra essere semplicemente un'altra sintassi per fare quasi la stessa operazione.
Ruslan,

2
@Ruslan Perché usare una sintassi speciale per una chiamata specifica quando hai già una soluzione generale che funziona ugualmente bene in tutti i casi? Stiamo solo applicando la stessa idea da map and co anche qui.
Voo

1
@Ruslan ... un'API restituisce valori null anziché raccolte o matrici vuote. Ad esempio, puoi fare qualcosa del genere (supponendo una classe Parent per la quale getChildren () restituisce un set di figli, oppure null se non ci sono figli): Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet); Ora posso elaborare in childrenmodo uniforme, ad es children.forEach(relative::sendGift);. Coloro che potrebbe anche essere combinate: Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);. Tutta la piastra di controllo null, ecc., ...
Joshua Taylor,

1
@Ruslan ... è delegato al codice provato e vero nella libreria standard. Ciò riduce la quantità di codice che io, come programmatore, devo scrivere, testare, eseguire il debug, ecc.
Joshua Taylor,

1
Sono contento che sto usando Swift. if let employee = employeeService.employee {print (employee.Id); }
gnasher729,

23

Non c'è quasi nessun valore aggiunto nell'avere un Optionalvalore singolo. Come hai visto, questo sostituisce solo un controllo per nullcon un controllo per presenza.

C'è un enorme valore aggiunto nell'avere una Collectiondi queste cose. Tutti i metodi di streaming introdotti per sostituire i loop di basso livello vecchio stile comprendono le Optionalcose e fanno le cose giuste su di esse (non elaborandole o alla fine restituendo un altro non impostato Optional). Se hai a che fare con n oggetti più spesso che con singoli oggetti (e il 99% di tutti i programmi realistici lo fanno), è un enorme miglioramento avere il supporto integrato per le cose facoltativamente presenti. Nel caso ideale non devi mai controllare null o presenza.


24
A parte le raccolte / i flussi, un Opzionale è anche ottimo per rendere le API più autodocumentate, ad es . Employee getEmployee()Vs. Optional<Employee> getEmployee()Mentre tecnicamente entrambi potrebbero tornare null, uno di questi ha molte più probabilità di causare problemi.
amon

16
C'è un sacco di valore in un optional di un singolo valore. Essere in grado di farlo .map()senza dover verificare la presenza di null lungo la strada è fantastico. Ad es Optional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);.
Joshua Taylor,

9
Non sono d'accordo con il tuo commento iniziale di nessun valore aggiunto. Opzionale fornisce contesto al tuo codice. Un rapido sguardo può dirti sulla correttezza di una funzione e tutti i casi sono coperti. Considerando che con null check, dovresti null null ogni singolo tipo di riferimento su ogni singolo metodo? Un caso simile può essere applicato a Classi e modificatori pubblici / privati; aggiungono valore zero al tuo codice, giusto?
Art

3
@JoshuaTaylor Onestamente, rispetto a quell'espressione , preferisco il vecchio controllo per null.
Kilian Foth,

2
Un altro grande vantaggio di Facoltativo anche con valori singoli è che non puoi dimenticare di controllare null quando dovresti farlo. Altrimenti è molto facile dimenticare l'assegno e finire con una NullPointerException ...
Sean Burton,

17

Finché usi Optionalesattamente come un'API di fantasia isNotNull(), quindi sì, non troverai differenze con il solo controllo di null.

Le cose non si dovrebbe usare Optionalper

Usare Optionalsolo per verificare la presenza di un valore è Bad Code ™:

// BAD CODE ™ --  just check getEmployee() != null  
Optional<Employee> employeeOptional =  Optional.ofNullable(employeeService.getEmployee());  
if(employeeOptional.isPresent()) {  
    Employee employee = employeeOptional.get();  
    System.out.println(employee.getId());
}  

Cose che dovresti usare Optionalper

Evitare che un valore non sia presente, producendo uno quando necessario quando si utilizzano metodi API con ritorno null:

Employee employee = Optional.ofNullable(employeeService.getEmployee())
                            .orElseGet(Employee::new);
System.out.println(employee.getId());

Oppure, se la creazione di un nuovo dipendente è troppo costosa:

Optional<Employee> employee = Optional.ofNullable(employeeService.getEmployee());
System.out.println(employee.map(Employee::getId).orElse("No employee found"));

Inoltre, rendere tutti consapevoli che i propri metodi potrebbero non restituire un valore (se, per qualsiasi motivo, non è possibile restituire un valore predefinito come sopra):

// Your code without Optional
public Employee getEmployee() {
    return someCondition ? null : someEmployee;
}
// Someone else's code
Employee employee = getEmployee(); // compiler doesn't complain
// employee.get...() -> NPE awaiting to happen, devs criticizing your code

// Your code with Optional
public Optional<Employee> getEmployee() {
    return someCondition ? Optional.empty() : Optional.of(someEmployee);
}
// Someone else's code
Employee employee = getEmployee(); // compiler complains about incompatible types
// Now devs either declare Optional<Employee>, or just do employee = getEmployee().get(), but do so consciously -- not your fault.

E infine, ci sono tutti i metodi simili a stream che sono già stati spiegati in altre risposte, anche se quelli non sono proprio tu che decidi di utilizzare Optionalma facendo uso di quelli Optionalforniti da qualcun altro.


1
@Kapep In effetti. Abbiamo discusso di questo argomento su / r / java e ho detto : «Vedo Optionalun'occasione persa. "Ecco, ho fatto questa nuova Optionalcosa che ho fatto in modo che tu sia costretto a controllare i valori null. Oh, e comunque ... Ho aggiunto un get()metodo in modo da non dover effettivamente controllare nulla;);)" . (...) Optionalè bello come un'insegna al neon "QUESTA POTREBBE ESSERE NULL", ma la nuda esistenza del suo get()metodo lo paralizza » . Ma OP stava chiedendo ragioni da utilizzare Optional, non ragioni contrarie o difetti di progettazione.
walen,

1
Employee employee = Optional.ofNullable(employeeService.getEmployee());Nel tuo terzo frammento, il tipo non dovrebbe essere Optional<Employee>?
Charlie Harding,

2
@immibis In che modo paralizzato? Il motivo principale da utilizzare getè se devi interagire con un codice legacy. Non riesco a pensare a molte valide ragioni nel nuovo codice da usare get. Detto questo quando si interagisce con il codice legacy spesso non c'è alternativa, ma Walen ha ancora ragione che l'esistenza di getcause per i principianti a scrivere codice errato .
Voo

1
@immibis Non ne ho parlato Mapuna volta e non sono a conoscenza di nessuno che abbia apportato una modifica così drastica e non compatibile con le versioni precedenti, quindi sicuramente potresti progettare intenzionalmente API errate con Optional- non sono sicuro di ciò che provi. Un Optionalavrebbe senso per un certo tryGetmetodo però.
Voo

2
@Voo Mapè un esempio che tutti conoscono. Ovviamente non possono rendere Map.getun ritorno opzionale a causa della compatibilità all'indietro, ma puoi facilmente immaginare un'altra classe simile a una mappa che non avrebbe tali vincoli di compatibilità. Dire class UserRepository {Optional<User> getUserDetails(int userID) {...}}. Ora vuoi ottenere i dettagli dell'utente connesso e sai che esistono perché sono riusciti ad accedere e il tuo sistema non elimina mai gli utenti. Quindi lo fai theUserRepository.getUserDetails(loggedInUserID).get().
immibis,

12

Il punto chiave per me nell'utilizzo di Facoltativo è di chiarire quando lo sviluppatore deve verificare se la funzione restituisce valore o meno.

//service 1
Optional<Employee> employeeOptional = employeeService.getEmployee();
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

//service 2
Employee employee = employeeService.getEmployeeXTPO();
System.out.println(employee.getId());

8
Mi sorprende che tutte le eleganti cose funzionali con gli optional, sebbene siano davvero belle da avere, sono considerate più importanti di questo punto proprio qui. Prima di Java 8, dovevi scavare attraverso Javadoc per sapere se dovevi annullare la verifica della risposta da una chiamata. Posta java 8, sai dal tipo di ritorno se riesci a ottenere "niente" indietro o no.
Buhb,

3
Bene, c'è il problema del "vecchio codice" in cui le cose potrebbero ancora restituire null ... ma sì, essere in grado di dire dalla firma è fantastico.
Haakon Løtveit,

5
Sì, sicuramente funziona meglio nelle lingue in cui l'opzione <T> è l'unico modo per esprimere la mancanza di un valore, ovvero le lingue senza null ptr.
Dureuill,

8

Il tuo errore principale è che stai ancora pensando in termini più procedurali. Questo non è inteso come una critica a te come persona, è semplicemente un'osservazione. Pensare in termini più funzionali arriva con il tempo e la pratica, e quindi i metodi sonoPresent e sembrano le cose più ovvie e corrette da chiamare per te. Il tuo errore secondario secondario sta creando l'Opzionale all'interno del tuo metodo. L'opzionale ha lo scopo di aiutare a documentare che qualcosa può o meno restituire un valore. Non potresti ottenere nulla.

Questo ti ha portato a scrivere un codice perfettamente leggibile che sembra perfettamente ragionevole, ma sei stato sedotto dai vili tentatori gemelli che ottengono ed èPresente.

Naturalmente la domanda diventa rapidamente "perché isPresent e arriva anche lì?"

Una cosa che molte persone qui mancano è che isPresent () è che non è qualcosa che è fatto per il nuovo codice scritto da persone completamente a bordo con come sono le lambda dannatamente utili e a chi piace la cosa funzionale.

Tuttavia, ci offre alcuni (due) buoni, fantastici, glamour (?) Vantaggi:

  • Facilita la transizione del codice legacy per utilizzare le nuove funzionalità.
  • Facilita le curve di apprendimento di Opzionale.

Il primo è piuttosto semplice.

Immagina di avere un'API simile a questa:

public interface SnickersCounter {
  /** 
   * Provides a proper count of how many snickers have been consumed in total.
   */
  public SnickersCount howManySnickersHaveBeenEaten();

  /**
    * returns the last snickers eaten.<br>
    * If no snickers have been eaten null is returned for contrived reasons.
    */
  public Snickers lastConsumedSnickers();
}

E hai avuto una classe legacy usando questo come tale (riempi gli spazi):

Snickers lastSnickers = snickersCounter.lastConsumedSnickers();
if(null == lastSnickers) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers);
}

Un esempio inventato per essere sicuro. Ma abbi pazienza qui.

Java 8 è stato lanciato e stiamo cercando di salire a bordo. Quindi una delle cose che facciamo è che vogliamo sostituire la nostra vecchia interfaccia con qualcosa che renda Opzionale. Perché? Perché come qualcun altro ha già gentilmente citato: questo elimina le congetture sul fatto che qualcosa possa essere nullo. Questo è già stato sottolineato da altri. Ma ora abbiamo un problema. Immagina di avere (mi scusi mentre premo alt + F7 su un metodo innocente), 46 posti in cui questo metodo viene chiamato in un codice legacy ben testato che fa un lavoro eccellente altrimenti. Ora devi aggiornare tutti questi.

QUESTO è dove brilla isPresent.

Perché ora: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {throw new NoSuchSnickersException (); } else {consumer.giveDiabetes (lastSnickers); }

diventa:

Optional<Snickers> lastSnickers = snickersCounter.lastConsumedSnickers();
if(!lastSnickers.isPresent()) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers.get());
}

E questo è un semplice cambiamento che puoi dare al nuovo junior: può fare qualcosa di utile e esplorerà la base di codice allo stesso tempo. win-win. Dopotutto, qualcosa di simile a questo modello è piuttosto diffuso. E ora non devi riscrivere il codice per usare lambdas o altro. (In questo caso particolare sarebbe banale, ma lascio pensare a esempi in cui sarebbe difficile come esercizio per il lettore.)

Nota che ciò significa che il modo in cui lo hai fatto è essenzialmente un modo per gestire il codice legacy senza effettuare costose riscritture. E il nuovo codice?

Bene, nel tuo caso, dove vuoi solo stampare qualcosa, dovresti semplicemente fare:

. SnickersCounter.lastConsumedSnickers () ifPresent (System.out :: println);

Che è piuttosto semplice e perfettamente chiaro. Il punto che gorgoglia lentamente verso la superficie, quindi, è che esistono casi d'uso per get () e isPresent (). Sono lì per permetterti di modificare meccanicamente il codice esistente per utilizzare i tipi più recenti senza pensarci troppo. Ciò che stai facendo è quindi errato nei seguenti modi:

  • Stai chiamando un metodo che potrebbe restituire null. L'idea corretta sarebbe che il metodo restituisca null.
  • Stai usando i metodi legid legacy per gestire questo opzionale, invece di usare i nuovi gustosi metodi che contengono fantasia di lambda.

Se si desidera utilizzare Opzionale come semplice controllo di sicurezza null, ciò che si dovrebbe fare è semplicemente questo:

new Optional.ofNullable(employeeServive.getEmployee())
    .map(Employee::getId)
    .ifPresent(System.out::println);

Naturalmente, la bella versione di questo sembra:

employeeService.getEmployee()
    .map(Employee::getId)
    .ifPresent(System.out::println);

A proposito, anche se non è assolutamente necessario, ti consiglio di utilizzare una nuova riga per operazione, in modo che sia più facile da leggere. Facile da leggere e comprendere batte la concisione in qualsiasi giorno della settimana.

Questo è ovviamente un esempio molto semplice in cui è facile capire tutto ciò che stiamo cercando di fare. Non è sempre così semplice nella vita reale. Ma nota come in questo esempio, ciò che stiamo esprimendo sono le nostre intenzioni. Vogliamo ottenere il dipendente, ottenere il suo documento d'identità e, se possibile, stamparlo. Questa è la seconda grande vittoria con Opzionale. Ci consente di creare un codice più chiaro. Penso anche che fare cose come fare un metodo che faccia un sacco di cose in modo da poterlo alimentare su una mappa è in generale una buona idea.


1
Il problema qui è che provi a far sembrare che queste versioni funzionali siano leggibili tanto quanto le versioni precedenti, in un caso anche dire che un esempio era "perfettamente chiaro". Questo non è il caso. Quell'esempio era chiaro, ma non perfettamente , poiché richiede più pensiero. map(x).ifPresent(f)semplicemente non ha lo stesso tipo di senso intuitivo che if(o != null) f(x)ha. La ifversione è perfettamente chiara. Questo è un altro caso di persone che saltano su un popolare carro-carro, * non * un caso di progressi reali.
Aaron,

1
Si noti che non sto dicendo che non vi è alcun vantaggio nell'uso della programmazione funzionale o di Optional. Mi riferivo solo all'uso dell'opzionale in questo caso specifico, e più ancora alla leggibilità, poiché più persone si comportano come questo formato è uguale o più leggibile di if.
Aaron,

1
Dipende dalle nostre nozioni di chiarezza. Mentre fai ottimi punti, vorrei anche sottolineare che per molte persone le lambda erano nuove e strane ad un certo punto. Oserei scommettere su un cookie di Internet che molte persone dicono siaPresent e ottengono un delizioso sollievo: ora potrebbero continuare con l'aggiornamento del codice legacy e quindi apprendere la sintassi lambda in seguito. D'altra parte, le persone con Lisp, Haskell o esperienza linguistica simile erano probabilmente felici di poter ora applicare schemi familiari ai loro problemi a java ...
Haakon Løtveit

@Aaron - la chiarezza non è il punto dei tipi opzionali. Personalmente trovo che non riducano la chiarezza, e ci sono alcuni casi (anche se non in questo caso) in cui la aumentano, ma il vantaggio principale è che (fintanto che eviti di usarlo isPresente getquanto è realisticamente possibile) essi migliorare la sicurezza . È più difficile scrivere un codice errato che non controlla l'assenza di un valore se il tipo è Optional<Whatever>invece di usare un nullable Whatever.
Jules

Anche se si prende immediatamente i valori fuori, finché non si utilizzaget , si è costretti a scegliere cosa fare quando si verifica un valore mancante: si dovrebbero o utilizzare orElse()(o orElseGet()) per la fornitura di un default, o orElseThrow()di gettare un scelto eccezione che documenta la natura del problema , che è meglio di un NPE generico che non ha senso fino a quando non si scava nella traccia dello stack.
Jules

0

Non vedo alcun vantaggio nello stile 2. Devi ancora capire che hai bisogno del controllo null e ora è ancora più grande, quindi meno leggibile.

Lo stile migliore sarebbe, se employeeServive.getEmployee () restituirebbe Opzionale e il codice diventerebbe:

Optional<Employee> employeeOptional = employeeServive.getEmployee();
if(employeeOptional.isPresent()){
    Employee employee= employeeOptional.get();
    System.out.println(employee.getId());
}

In questo modo non puoi callemployeeServive.getEmployee (). GetId () e noti che dovresti gestire il valore facoltativo in qualche modo. E se nel tuo intero codebase i metodi non restituiscono mai null (o quasi mai con eccezioni ben note al tuo team a questa regola), aumenterà la sicurezza contro NPE.


12
Opzionale diventa una complicazione inutile nel momento in cui lo usi isPresent(). Usiamo opzionale quindi non dobbiamo testare.
candied_orange,

2
Come altri hanno già detto, non usare isPresent(). Questo è l'approccio sbagliato agli opzionali. Usa mapo flatMapinvece.
Andres F.

1
@CandiedOrange Eppure la risposta più votata (dicendo di usare ifPresent) è solo un altro modo per testare.
immibis,

1
@CandiedOrange ifPresent(x)è altrettanto un test if(present) {x}. Il valore restituito è irrilevante.
immibis,

1
@CandiedOrange Tuttavia, originariamente hai detto "quindi non dobbiamo testare". Non puoi dire "quindi non dobbiamo testare" quando, di fatto, stai ancora testando ... quale sei.
Aaron,

0

Credo che il più grande vantaggio è che se la vostra firma metodo restituisce un Employeevoi non controllare per nulla. Sai con quella firma che è garantito per restituire un dipendente. Non è necessario gestire un caso di errore. Con un optional sai che lo fai.

Nel codice che ho visto ci sono controlli null che non falliranno mai poiché le persone non vogliono tracciare il codice per determinare se un null è possibile, quindi lanciano controlli null ovunque in modo difensivo. Questo rende il codice un po 'più lento, ma soprattutto rende il codice molto più rumoroso.

Tuttavia, affinché funzioni, è necessario applicare questo modello in modo coerente.


1
Il modo più semplice per raggiungere questo obiettivo è l'utilizzo @Nullablee @ReturnValuesAreNonnullByDefaultinsieme all'analisi statica.
maaartinus,

@maaartinus Aggiunta di ulteriori strumenti sotto forma di analisi statica e documentazione nelle annotazioni del decoratore, è più semplice quindi compilare il supporto API in tempo?
Slitta

L'aggiunta di strumenti aggiuntivi è in effetti più semplice, in quanto non richiede modifiche al codice. Inoltre, vuoi avere comunque come cattura anche altri bug. +++ Ho scritto una sceneggiatura banale aggiunta package-info.javacon @ReturnValuesAreNonnullByDefaulta tutti i miei pacchetti, quindi tutto quello che devo fare è aggiungere @Nullabledove si deve passare a Optional. Ciò significa meno caratteri, nessuna immondizia aggiunta e nessun sovraccarico di runtime. Non devo cercare la documentazione come mostra quando si passa il mouse sopra il metodo. Lo strumento di analisi statica rileva gli errori e le successive modifiche alla nullità.
Maaartinus,

La mia più grande preoccupazione Optionalè che aggiunge un modo alternativo di gestire la nullabilità. Se fosse stato in Java sin dall'inizio, potrebbe andare bene, ma ora è solo una seccatura. Seguire la via di Kotlin sarebbe molto meglio IMHO. +++ Nota che List<@Nullable String>è ancora un elenco di stringhe, mentre List<Optional<String>>non lo è.
maaartinus,

0

La mia risposta è: basta. Almeno pensaci due volte se è davvero un miglioramento.

Un'espressione (presa da un'altra risposta) come

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

sembra essere il giusto modo funzionale di trattare con metodi di ritorno originariamente nulli. Sembra bello, non getta mai e non ti fa mai pensare a maneggiare il caso "assente".

Sembra meglio del vecchio stile

Employee employee = employeeService.getEmployee();
if (employee != null) {
    ID id = employee.getId();
    if (id != null) {
        System.out.println(id);
    }
}

il che rende evidente che potrebbero esserci delle elseclausole.

Lo stile funzionale

  • sembra completamente diverso (ed è illeggibile per le persone non utilizzate)
  • è un dolore per il debug
  • non può essere facilmente esteso per gestire il caso "assente" ( orElsenon è sempre sufficiente)
  • induce in errore a ignorare il caso "assente"

In nessun caso dovrebbe essere usato come una panacea. Se fosse universalmente applicabile, la semantica .dell'operatore dovrebbe essere sostituita dalla semantica ?.dell'operatore , l'NPE dimenticato e tutti i problemi ignorati.


Pur essendo un grande fan di Java, devo dire che lo stile funzionale è solo un sostituto della dichiarazione di un povero Java

employeeService
.getEmployee()
?.getId()
?.apply(id => System.out.println(id));

ben noto da altre lingue. Siamo d'accordo che questa affermazione

  • non è (davvero) funzionale?
  • è molto simile al codice vecchio stile, solo molto più semplice?
  • è molto più leggibile dello stile funzionale?
  • è il debugger-friendly?

Sembra che tu stia descrivendo l' implementazione di C # di questo concetto con una caratteristica del linguaggio completamente diversa dall'implementazione di Java con una classe di libreria core. Mi sembrano uguali
Caleth,

@Caleth Assolutamente no. Potremmo parlare del "concetto di semplificare una valutazione nulla-sicura", ma non lo definirei un "concetto". Possiamo dire che Java implementa la stessa cosa usando una classe di libreria core, ma è solo un casino. Basta iniziare employeeService.getEmployee().getId().apply(id => System.out.println(id));e renderlo null-safe una volta usando l'operatore Navigazione sicura e una volta usando il Optional. Certo, potremmo chiamarlo un "dettaglio di implementazione", ma l'implementazione è importante. Ci sono casi in cui i lama rendono il codice più semplice e piacevole, ma qui è solo un brutto abuso.
maaartinus,

Libreria di classi: Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);caratteristica del linguaggio: employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));. Due sintassi, ma finiscono per assomigliare molto a me. E il "concetto" è Optionalaka NullableakaMaybe
Caleth

Non capisco perché pensi che uno di questi sia "brutto abuso" e l'altro buono. Potrei capirti pensando sia "brutti abusi" o entrambi buoni.
Caleth,

@Caleth Una differenza è che invece di aggiungere due punti interrogativi, devi riscriverlo tutto. Il problema non è che la funzione Lingua e i codici di classe Libreria differiscono molto. Va bene. Il problema è che il codice originale e i codici di classe Library differiscono molto. Stessa idea, codice diverso. È troppo male. +++Ciò che viene abusato sono i lama per gestire la nullità. I Lamdas stanno bene, ma Optionalnon lo sono. La nullità necessita semplicemente di un adeguato supporto linguistico come in Kotlin. Un altro problema: List<Optional<String>>non è correlato a List<String>, dove avremmo bisogno che siano collegati.
maaartinus,

0

Opzionale è un concetto (un tipo di ordine superiore) proveniente dalla programmazione funzionale. Usarlo elimina il controllo nidificato nidificato e ti salva dalla piramide del destino.

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.