2015-09-18 20 views
7

Jaki jest najlepszy sposób, aby funkcjonalnie skomponować java Function i Consumer? Na przykład podając niektóre Function<Object, String> f i niektóre Consumer<String> c, a następnie f.andThen(c) byłoby naturalne, ale nie jest tak, jak działają interfejsy.Tworzenie funkcji Java i konsumenta

Dwie opcje, które widzę są albo zastąpić Consumer<String> c z Function<String, Void> c lub zmienić Consumer<String> c do BiConsumer<Function<Object, String>, String> c i zrobić

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

jest jednym z nich lepiej niż inne? Czy istnieje lepszy sposób?

+0

Czy możesz podać do tego przypadek? – Tunaki

+0

Napisałbym o tym swoją własną klasę "Konsumenta". Zajmie to około 10 linii kodu. –

+1

@Tunaki prosty przypadek użycia byłoby, gdybyśmy mieli obiekt chcieliśmy serializować jako ciąg znaków na różne sposoby (funkcja), a następnie chcieliśmy wyprowadzić go gdzieś jak do bazy danych lub do terminala (konsumenta) – kag0

Odpowiedz

10

Czy to właśnie próbujesz zrobić?

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

Jeśli znajdziesz się w obliczu tego problemu dużo, można dokonać statycznej metody, aby to zrobić (ale jest to naprawdę warto?):

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

Czasami warto: możesz użyć dwóch fajnych referencji metod zamiast pojedynczego nudnego lambda. Jeśli twoje odwołania do metod są powiązane z instancją, a instancja jest przechowywana w zmiennej nieostatniej, to wersja lambda może wyglądać naprawdę niezgrabnie. W tym kontekście proponowana statyczna metoda jest nawet lepsza niż nieistniejąca domyślna metoda 'and Then (Consumer)', ponieważ typ odwołania do metody zostanie wywnioskowany automatycznie. –

+1

@TagirValeev Zgadzam się, umiejętność używania referencji metod jest dużym plusem. Po prostu nie natrafiam na sytuacje 'Function' -Konsumer' często, ale być może inni. To, co robię cały czas, to potrzeba skomponowania "Function" i "Predicate". – Misha

0

nie jest to zrealizowane przy użyciu map i 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

Zasadniczo nic nie uruchamiasz, ponieważ twój strumień brakuje operacji terminalu. Również przetwarzanie strumieniowe jest tylko przykładem użycia 'Konsumenta'. Mogą być przydatne także w innych miejscach. –

+0

@TagirValeev - Interesujące - dodanie '.toArray()' wydaje się jednak naprawić. – OldCurmudgeon

+2

To jest nadużycie 'peek'. Po prostu wykonaj '.map (f) .forEach (c)' lub '.map (f) .forEachOrdered (c)' jeśli 'c' nie jest wątkowo bezpieczne. – Misha

0

Możesz utworzyć kopię interfejsu java.util.function.Function we własnym kodzie. Call it ConsumableFunction i zmień wszystkie domyślne typy parametrów metod i zwróć wartości z Function na ConsumableFunction. Następnie dodaj następującą funkcję domyślną:

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

Teraz pozwól, aby wszystkie implemflekcje funkcji zaimplementowały funkcję ConsumableFunction. Następnie możesz napisać kod w następujący sposób:

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