Применить JDK 8 Consumer on String

У меня есть вопрос относительно Java 8. Вот мой исходный код:

final Consumer<String> d = e -> System.out.println(e);
final Function<String, String> upper = x -> x.toUpperCase();
final Function<String, String> lower = x -> x.toLowerCase();

new Thread(() -> d.accept(upper.apply("hello 1"))).run();
new Thread(() -> d.accept(lower.apply("hello 2"))).run();

Это работает достаточно хорошо и производит следующий вывод:

HELLO 1
hello 2

Теперь мой вопрос, если синтаксис выше d.accept и upper.apply является единственным возможным, или если есть еще один стиль java 8 лямбда, мы могли бы написать последние две строки.

Ответ 1

Прежде чем говорить что-либо о лямбда-выражениях или функциональных интерфейсах, мы должны поговорить о вашей действительно проблематичной ошибке: вы вызываете run() в потоке! Если вы хотите запустить новый поток, вы должны вызвать start() в экземпляре Thread, если вы хотите последовательно запускать код, не создавайте Thread (но просто Runnable).

Тем не менее, существует несколько методов default на функциональных интерфейсах Java 8 для объединения функций, например. вы можете связать два Function с помощью Function.andThen(…), но доступные комбинации далеки от завершения.

Если некоторая комбинирующая задача повторяется в вашем приложении, вы можете подумать о создании методов утилиты:

public static <T> Runnable bind(T value, Consumer<T> c) {
    return ()->c.accept(value);
}
public static <T,U> Consumer<U> compose(Function<U,T> f, Consumer<? super T> c) {
    return u->c.accept(f.apply(u));
}

new Thread(bind("Hello 1", compose(upper, d))).start();
new Thread(bind("Hello 2", compose(lower, d))).start();

Но эти три части больше напоминают задачу для API потока:

Stream.of("Hello 1").map(upper).forEach(d);
Stream.of("Hello 2").map(lower).forEach(d);

Я оставил здесь создание нового потока, так как он все равно не имеет никакой пользы.

Если вам действительно нужна параллельная обработка, вы можете сделать это на основе символов:

"Hello 1".chars().parallel()
  .map(Character::toUpperCase).forEachOrdered(c->System.out.print((char)c));

но все равно не будет никакой выгоды, учитывая простоту задачи и фиксированные накладные расходы на параллельную обработку.

Ответ 2

Вы также можете написать вот так:

    new Thread(() -> Stream.of("hello 1").map(upper).forEach(d)).run();
    new Thread(() -> Stream.of("hello 1").map(lower).forEach(d)).run();

Или больше directlry:

    new Thread(() -> Stream.of("hello 1").map(String::toUpperCase).forEach(System.out::println)).run();
    new Thread(() -> Stream.of("hello 1").map(String::toLowerCase).forEach(System.out::println)).run();

Ответ 3

Да, это единственный возможный синтаксис. Фактически, когда вы используете лямбда, вы даже не знаете, действительно ли это лямбда или просто обычная реализация данного интерфейса (через анонимный класс или даже обычный класс). Таким образом, вы должны использовать функциональный интерфейс как любой другой интерфейс Java: вызывать его метод явно.