2015-09-18 22 views
7

Qual è il modo migliore per comporre funzionalmente un java Function e uno Consumer? Ad esempio, dato un po 'di Function<Object, String> f e un po' di Consumer<String> c, allora fare f.andThen(c) sarebbe naturale, ma non è così che funzionano le interfacce.Composizione di una funzione Java e consumer

Le due opzioni che vedo sono o sostituire Consumer<String> c con Function<String, Void> c o cambiare Consumer<String> c-BiConsumer<Function<Object, String>, String> c e fanno

accept(Function<Object, String> f, Object o) { 
    String thing = f.apply(o); 
    //do something with thing 
} 

è uno di questi migliore rispetto agli altri? C'è un modo migliore?

+0

Puoi fornire una custodia per questo? – Tunaki

+0

Scriverò la mia classe 'Consumer' per questo. Ci vorrebbero circa 10 righe di codice. –

+1

@Tunaki un caso d'uso semplice sarebbe se avessimo un oggetto che volevamo serializzare come una stringa modi diversi (la funzione) e quindi volevamo farlo uscire da qualche parte come nel database o nel terminale (il consumatore) – kag0

risposta

10

È questo che stai cercando di fare?

Consumer<Object> composed = o -> c.accept(f.apply(o)); 

Se vi trovate di fronte a questo problema molto, è possibile effettuare un metodo statico per fare questo (ma è davvero vale la pena?):

static<T,R> Consumer<T> applyAndAccept(Function<? super T,? extends R> f, Consumer<R> c){ 
    return t -> c.accept(f.apply(t)); 
} 
+2

A volte vale la pena: puoi utilizzare due riferimenti al metodo piuttosto che un singolo lambda alesaggio. Se i tuoi riferimenti al metodo sono associati ad un'istanza e l'istanza è memorizzata nella variabile non finale, allora la versione lambda potrebbe sembrare davvero maldestra. In questo contesto, il metodo statico proposto è persino migliore del metodo predefinito non esistente 'eThen (Consumer)', poiché il tipo di riferimento del metodo verrà dedotto automaticamente. –

+1

@TagirValeev Sono d'accordo, la possibilità di utilizzare i riferimenti al metodo è un grande vantaggio. Non mi imbatto abbastanza spesso nella situazione di 'Function'-'Consumer', ma forse altri lo fanno. Ciò che mi aggrada sempre è la necessità di comporre 'Function' e' Predicate'. – Misha

0

Non è questo compiuto utilizzando map e forEach?

public void test() { 
    Function<Object, String> f = (Object t) -> t.toString(); 
    Consumer<String> c = (String t) -> { 
     System.out.println(t); 
    }; 
    List<Object> data = new ArrayList<>(); 
    data.stream().map(f).forEach(c); 
} 
+1

In pratica non esegui nulla, perché il tuo stream manca l'operazione del terminale. Anche l'elaborazione del flusso è solo un esempio dell'utilizzo di 'Consumer'. Possono essere utili anche in altri posti. –

+0

@TagirValeev - Interessante - l'aggiunta di '.toArray()' sembra risolvere il problema. – OldCurmudgeon

+2

Questo è un abuso di 'peek'. Basta fare '.map (f) .forOach (c)' o '.map (f) .forEachOrdered (c)' if 'c' non è thread-safe. – Misha

0

È possibile eseguire una copia dell'interfaccia di java.util.function.Function nel proprio codice. Chiamarlo ConsumableFunction e modificare tutti i tipi di parametri del metodo di default e restituire i valori da Function a ConsumableFunction. Quindi aggiungere la seguente funzione di default:

default Consumer<T> atLast(Consumer<R> consumer) { 
    Objects.requireNonNull(consumer); 
    return (T t) -> consumer.accept(apply(t)); 
} 

Ora tutti i tuoi implmementations funzione implementano ConsumableFunction invece. Quindi puoi scrivere un codice come questo:

Consumer<A> consumerForA = functionFromAToB 
    .andThen(functionFromBToC) 
    .andThen(functionFromCToD) 
    .atLast(consumerForD);