2016-04-22 3 views
8

Quindi, supponendo che uso i filtri casuale in un flusso, il modo più semplice è quello di immettere direttamente il predicato:Qual è la differenza tra l'utilizzo di un predicato o una funzione come filtro di flusso Java?

x.stream().filter(e -> e % 2 == 0) 

Così posso semplicemente fare riferimento e definire il predicato in anticipo:

Predicate<Integer> isEven = e -> e % 2 == 0; 
... 
x.stream().filter(isEven) 

Ma posso anche usare una funzione:

private static boolean isEven(Integer integer) { 
    return integer % 2 == 0; 
} 
... 
x.stream().filter(MyClass::isEven) 

Per quanto posso dire, il predicato è, naturalmente, molto più limitata, mentre il la funzione potrebbe avere effetti collaterali ecc. Ma poiché persone come Venkat Subramaniam usano quest'ultima soluzione, mi chiedo davvero: quali sono le differenze principali qui?

risposta

11

No! Il predicato non è molto limitato rispetto a un riferimento al metodo! In effetti, queste cose sono tutte uguali!

Basta guardare la firma filter() funzione di: filter(Predicate<? super T> predicate)

E prendiamo in considerazione i vostri esempi:

x.stream().filter(e -> e % 2 == 0)

Predicate<Integer> isEven = e -> e % 2 == 0; 
... 
x.stream().filter(isEven) 

Il primo è solo una versione inline di quest'ultimo.

private static boolean isEven(Integer integer) { 
return integer % 2 == 0; 
} 
... 
x.stream().filter(MyClass::isEven) 

e qui si può vedere Method References in azione. MR sono solo uno zucchero sintattico che ti consente di definire l'espressione lambda basata su funzioni già esistenti.

Alla fine della giornata tutte quelle espressioni diventano la stessa implementazione dell'interfaccia funzionale Predicate.

Inoltre, è anche possibile eseguire effetti collaterali nei tuoi Lambda Expressions utilizzando la sintassi del blocco sul lato destro, ma non è generalmente consigliato:

e -> { 
    //side effects here 
    return e % 2 == 0; 
} 
+0

Vedo che senza MR saremo in grado di definire solo predicati che sono veramente limitati per quanto riguarda la loro riusabilità. Quindi questo porta alla domanda qual è il modo più pulito di scrivere? Usando un MR, un Predicato o la stessa affermazione? – AdHominem

+1

@AdHominem dipende dal tuo contesto. Se non si intende riutilizzare il predicato, attenersi a un'espressione lambda semplice, ma se si intende continuare a riutilizzarlo, suggerisco di creare un'istanza Predicate statico in alcune classi utils senza creare un metodo stesso –

4

Se si guarda dal punto di vista della costruzione di una biblioteca dei Predicati riutilizzabili, una libreria di funzioni che restituisce un valore booleano è una raccolta di predicati molto più versatile di una libreria di istanze Predicate finali statiche. Perché?

consideri una libreria che contiene quanto segue:

public static boolean isEven(int i) { return i -> i % 2 == 0; } 

vs.

public static final Predicate<Integer> IS_EVEN = i -> i % 2 == 0; 
  • se le funzioni di libreria sono chiamati così, leggono meglio. Quando rivisitando codice dopo una lunga assenza è più facile per la scansione filter(MyLib::isEven) di filter(i -> i % 2 == 0) e filter(MyLib::isEven) ti dice esattamente ciò che viene invocato, mentre filter(MyLib.IS_EVEN) non lo fa
  • Se si desidera solo per chiamare la funzione di libreria, invece di usarlo come un predicato, è più leggibile.MyLib.isEven(i) scansioni meglio di MyLib.IS_EVEN.test(i)
  • Se è necessario utilizzare un IntPredicate al posto di un Predicate<Integer> Guava Predicate<Integer>, Apache Collections4 Predicate<Integer>, ecc, con una funzione di libreria è sufficiente continuare a fare MyLib::isEven. Con un'istanza static final Predicate<Integer> si dovrà convertirlo facendo MyLib.IS_EVEN::test (e si finisce per utilizzare un riferimento metodo in ogni caso)

Lo stesso ragionamento si applica a tutti i tipi funzionali. Scrivi funzioni. Possono essere applicati a qualsiasi tipo funzionale che corrisponda alla firma con un semplice riferimento al metodo.

Problemi correlati