Преобразование пользователей в функции

Многие lambdas для интерфейса Function принимают форму

t -> {
    // do something to t
    return t;
}

Я делаю это так часто, что написал для него такой метод.

static <T> Function<T, T> consumeThenReturn(Consumer<T> consumer) {
    return t -> {
        consumer.accept(t);
        return t;
    };
}

Это позволяет мне делать действительно приятные вещи, такие как:

IntStream.rangeClosed('A', 'Z')
         .mapToObj(a -> (char) a)
         .collect(Collectors.collectingAndThen(Collectors.toList(), consumeThenReturn(Collections::shuffle)))
         .forEach(System.out::print); 

Есть ли другой способ сделать такие преобразования, не полагаясь на мой собственный метод? Есть ли что-нибудь в новых API, которые я пропустил, что делает мой метод избыточным?

Ответ 1

Существует много потенциально полезных методов, которые могут быть добавлены в интерфейсы Function, Consumer и Supplier. Вы даете хороший пример (преобразование Consumer в Function), но есть много других возможных конверсий или утилит, которые можно было бы добавить. Например, используя Function как Consumer (игнорируя возвращаемое значение) или как Supplier (путем предоставления входного значения). Или преобразование a BiFunction в Function путем подачи значения. Разумеется, все это можно сделать вручную в коде или предоставить через полезные функции, как вы показали, но, возможно, было бы полезно иметь стандартизированные механизмы в API, как это существует на многих других языках.

Это спекуляция с моей стороны, но я предполагаю, что это отражает желание дизайнеров языка оставить API как можно более чистым. Однако я заинтригован контрастом (как пример) очень богатым набором Comparator утилит, предоставляемых языком для отмены заказов, сравнения по нескольким критериям, обработки нулевых значений и т.д. Этим также может быть легко оставлено пользователь, но был предоставлен API. Мне было бы интересно услышать от одного из разработчиков языка, почему подходы к этим интерфейсам кажутся настолько непоследовательными.

Ответ 2

Коллекции коллекционеров Apache 4.x имеет то, что вы ищете, я думаю. Его эквиваленты для Function и Consumer равны соответственно Transformer и Closure, и он обеспечивает способ их компоновки с использованием ClosureTransformer

Тривиально преобразовать между эквивалентными функциональными типами, вызывая SAM одного и присваивая его другому. Объединяя все это, вы можете получить Java 8 Function следующим образом:

Consumer<String> c = System.out::println;
Function<String,String> f = ClosureTransformer.closureTransformer(c::accept)::transform;

c::accept преобразует Java 8 Consumer в эквивалентный Apache Commons 4 Closure, а окончательный ::transform преобразует Apache Commons 4 Transformer в эквивалентный Java 8 Function.