Come dovremmo gestire il flusso jdk8 per i valori nulli


88

Ciao amici sviluppatori Java,

So che l'argomento potrebbe essere un po 'in in advancequanto il JDK8 non è ancora stato rilasciato (e non per ora comunque ..) ma stavo leggendo alcuni articoli sulle espressioni Lambda e in particolare sulla parte relativa alla nuova API di raccolta nota come Stream.

Ecco l'esempio fornito nell'articolo di Java Magazine (è un algoritmo di popolazione di lontre ..):

Set<Otter> otters = getOtters();
System.out.println(otters.stream()
    .filter(o -> !o.isWild())
    .map(o -> o.getKeeper())
    .filter(k -> k.isFemale())
    .into(new ArrayList<>())
    .size());

La mia domanda è: cosa succede se nel mezzo dell'iterazione interna di Set, una delle lontre è nulla?

Mi aspetto che venga generata una NullPointerException, ma forse sono ancora bloccato nel precedente paradigma di sviluppo (non funzionale), qualcuno può illuminarmi su come dovrebbe essere gestito?

Se questo genera davvero un'eccezione NullPointerException, trovo la funzione piuttosto pericolosa e dovrà essere utilizzata solo come di seguito:

  • Sviluppatore per assicurarsi che non ci sia alcun valore nullo (magari utilizzando un precedente .filter (o -> o! = Null))
  • Sviluppatore per assicurarsi che l'applicazione non generi mai null otter o uno speciale oggetto NullOtter da gestire.

Qual è l'opzione migliore o qualsiasi altra opzione?

Grazie!


3
Direi che spetta al programmatore fare la cosa giusta qui; la JVM e il compilatore possono fare solo così tanto. Si noti che alcune implementazioni di raccolte non consentiranno tuttavia valori null.
fge

19
Puoi usare filter(Objects::nonNull)con Objectsdajava.utils
Benj

Risposte:


44

Il pensiero corrente sembra essere quello di "tollerare" i valori nulli, cioè di consentirli in generale, sebbene alcune operazioni siano meno tolleranti e potrebbero finire per generare NPE. Consulta la discussione sui valori nulli nella mailing list del gruppo di esperti di Lambda Libraries, in particolare questo messaggio . Successivamente è emerso il consenso sull'opzione n. 3 (con una notevole obiezione da parte di Doug Lea). Quindi sì, la preoccupazione del PO per le condutture che esplodono con NPE è valida.

Non per niente Tony Hoare ha definito i valori nulli "l'errore da un miliardo di dollari". Trattare con i null è un vero dolore. Anche con le raccolte classiche (senza considerare lambda o flussi) i valori null sono problematici. Come fge menzionato in un commento, alcune raccolte consentono null e altre no. Con le raccolte che consentono valori nulli, questo introduce ambiguità nell'API. Ad esempio, con Map.get () , un ritorno nullo indica che la chiave è presente e il suo valore è nullo o che la chiave è assente. Bisogna fare del lavoro extra per chiarire questi casi.

L'uso usuale di null è per denotare l'assenza di un valore. L'approccio per affrontare questo problema proposto per Java SE 8 consiste nell'introdurre un nuovo java.util.Optionaltipo, che incapsula la presenza / assenza di un valore, insieme a comportamenti di fornire un valore predefinito, o lanciare un'eccezione, o chiamare una funzione, ecc. Se il valore è assente. Optionalviene utilizzato solo dalle nuove API, tuttavia, tutto il resto nel sistema deve ancora sopportare la possibilità di null.

Il mio consiglio è di evitare per quanto possibile riferimenti nulli effettivi. È difficile vedere dall'esempio dato come potrebbe esserci una lontra "nulla". Ma se fosse necessario, i suggerimenti dell'OP di filtrare i valori nulli o mapparli su un oggetto sentinella (il modello di oggetto nullo ) sono ottimi approcci.


4
Perché evitare i valori nulli? Sono ampiamente utilizzati nei database.
Raffi Khatchadourian

6
@RaffiKhatchadourian Sì, i null vengono utilizzati nei database, ma sono ugualmente problematici. Guarda questo e questo e leggi tutte le risposte ei commenti. Considera anche l'impatto di SQL null sulle espressioni booleane: en.wikipedia.org/wiki/… ... questa è una ricca fonte di errori di query.
Stuart Marks

1
@RaffiKhatchadourian Perché fa nullschifo, ecco perché. Acc. a un sondaggio che ho visto (non riesco a trovarlo), NPE è l'eccezione numero 1 in Java. SQL non è un linguaggio di programmazione di alto livello, certamente non funzionale, che disprezza il null, quindi non gli interessa.
Abhijit Sarkar

95

Sebbene le risposte siano corrette al 100%, un piccolo suggerimento per migliorare la nullgestione dei casi dell'elenco stesso con Opzionale :

 List<String> listOfStuffFiltered = Optional.ofNullable(listOfStuff)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

La parte Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)ti consentirà di gestire bene il caso in cui listOfStuffè null e restituisce un emptyList invece di fallire con NullPointerException.


2
Mi piace, evita il controllo esplicito di null.
Chris

1
questo sembra più chiaro .. bello ed esattamente quello di cui avevo bisogno
Abdullah Al Noman

Sta verificando esplicitamente la presenza di null, solo in un modo diverso. Se è consentito essere nullo, dovrebbe essere un Opzionale, questa è l'idea, giusto? Ban tutti i valori nulli con gli optional. Inoltre, è una cattiva pratica restituire null invece di un elenco vuoto, quindi mostra due odori di codice se vedo questo: nessun Optionals utilizzato e nessun flusso vuoto restituito. E quest'ultimo è disponibile da oltre 20 anni, quindi è maturo ...
Koos Gadellaa

cosa succede se voglio restituire un elenco null invece vuoto?
Ashburn RK

@ AshburnRK è una cattiva pratica. Dovresti restituire un elenco vuoto.
Johnny

70

La risposta di Stuart fornisce un'ottima spiegazione, ma vorrei fornire un altro esempio.

Mi sono imbattuto in questo problema durante il tentativo di eseguire uno reducestream contenente valori nulli (in realtà lo era LongStream.average(), che è un tipo di riduzione). Dal momento che average () restituisce OptionalDouble, ho ipotizzato che Stream potesse contenere null, ma invece è stata generata un'eccezione NullPointerException. Ciò è dovuto alla spiegazione di Stuart di null v. Empty.

Quindi, come suggerisce l'OP, ho aggiunto un filtro in questo modo:

list.stream()
    .filter(o -> o != null)
    .reduce(..);

O come tangens sottolineato di seguito, utilizza il predicato fornito dall'API Java:

list.stream()
    .filter(Objects::nonNull)
    .reduce(..);

Dalla discussione della mailing list Stuart ha collegato: Brian Goetz sui null in Streams


19

Se si desidera solo filtrare i valori null da un flusso, è possibile utilizzare semplicemente un riferimento al metodo java.util.Objects.nonNull (Object) . Dalla sua documentazione:

Questo metodo esiste per essere utilizzato come predicato ,filter(Objects::nonNull)

Per esempio:

List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null);

list.stream()
    .filter( Objects::nonNull )  // <-- Filter out null values
    .forEach( System.out::println );

Questo stamperà:

Foo
Bar

7

Un esempio di come evitare null, ad esempio utilizzare il filtro prima di groupingBy

Filtra le istanze null prima di groupingBy.

Ecco un esempio

MyObjectlist.stream()
            .filter(p -> p.getSomeInstance() != null)
            .collect(Collectors.groupingBy(MyObject::getSomeInstance));
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.