Java 8: 将 Consumer<Y> 链接到 Function<X,Y>

11

探索Java 8的新功能,我想通过将Consumer<Y>链接到Function<X,Y>来创建一个Consumer<X>

这有意义吗?如果有的话,一个好的(通用)解决方案应该是什么样子的?


我尝试过的方法(举个特殊例子):

假设

@FunctionalInterface
public interface PartialFunction<X, Y> {
    Y apply(X x) throws Exception;
}

并且

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

public class PartialFunctions {

    public static <X, Y> Function<X, Optional<Y>> withOptionalResults(final PartialFunction<X, Y> funcThatThrows) {
        return z -> {
            try {
                return Optional.of(funcThatThrows.apply(z));
            } catch (final Exception e) {
                return Optional.empty();
            }
        };
    }

    public static <X, Y> Consumer<X> acceptOnSuccess(final PartialFunction<X, Y> g, final Consumer<Y> c) {
        return x -> withOptionalResults(x).apply(t).ifPresent(c);
    }
}

我最终可能使用的方式是:

files.forEach(PartialFunctions.<File, BufferedImage>acceptOnSuccess(
        ImageIO::read, images::add));

然而,需要明确的通用规范并不是最佳选择。希望有更好的解决方案?

1
我认为这样做很好。除了一个错别字外,在没有类型证明的情况下编译通过acceptOnSuccess的调用。我更喜欢使用 Optional作为这里的选择,而不是使用null - Maurice Naftalin
4个回答

10
interface IgnoreThrowing<F,V> extends Function<F,V> {
    public default V apply(F from) {
        try {
            return ignore(from);
        } catch(Exception e) {
            return null;
        }
    }
    public V ignore(F from) throws Exception;
}

class Throwables {
    public static <F,V> Function<F,V> ignore(IgnoreThrowing<F,V> f) {
        return f;
    }
}

static {
    files.map(Throwables.ignore(ImageIO::read)).collect(...)
}

如果您添加一个忽略空输入的收集器,情况会变得更好。

编辑:我在没有进行语法检查或编译的情况下编写了这篇文章,因此并不完全确定默认值的位置以及编译器是否能够成功推断链接函数类型参数。


感谢您提供有关defaults的提示! - Jens Piegsa
很酷的东西...你使用Throwables::Ignore的方式远不止于此答案/示例,非常有用。 - Alex Pakka
Throwables::Ignore(..) 应该改为 Throwables.Ignore(..) - aepurniet
我在使用IgnoreThrowing时遇到了一些问题,它被检测为一个函数式接口,但这似乎是我的IDE编译器的问题。 - Jens Piegsa
3
不要吞噬异常。不要与谷歌的Guava命名冲突。更好地命名你的类。(“IgnoreThrowing”是一个动词,而类应该是名词,比如“PropagatingFunctionDecorator”)。最后,在把一堆代码放到页面上之前,请先解释一下你的技术原理。 - Aleksandr Dubinsky
它只是用来展示一种方法,而不是在核反应堆中使用的。 - aepurniet

5
您可以像这样扩展Function接口:
public interface ComposableFunction<T, R> extends Function<T, R> {

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

}

然后像这样定期使用它:

ComposableFunction<Throwable, String> getMessage = Throwable::getMessage;
Consumer<String> log = System.out::println;
Consumer<Throwable> logMessage = getMessage.andThen(log);

2
你可以尝试这样做吗?
public void lambdaChaining() {
    System.out.println("\nlambda chaining:");
    List<String> l = Arrays.asList("1", "22", "333", "4444", "55555", "666666", "7777777", "88888888", "999999999");
    Function<String, ?> f = s -> s.length();
    Consumer<String> c = s -> System.out.println(f.apply(s));;
    l.forEach(c);
}

0

另一种选择(也是更一般的情况)是使用 new Function<X,Y>(...).andThen(new Function<Y,Z>(...))。我不会混合消费者和函数,而是链接函数然后馈送到消费者(这些消费者可以选择链接)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接