java 中的 Callable 与 Supplier 接口

Callable vs Supplier interface in java

CallableSupplier包中的java.util.concurrentjava.util.function函数接口分别有如下签名-

public interface Callable<V> {
    V call() throws Exception;
}

public interface Supplier<T> {
    T get();
}

是否有一些特定的用例,其中每一个都比另一个更适合?

它们在用法上的区别可以从各自的文档中看出:

Callable:

A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call.

The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread.

Supplier:

Represents a supplier of results.

There is no requirement that a new or distinct result be returned each time the supplier is invoked.

这意味着 Callable.call 的调用者期望抛出异常并将相应地处理异常。这对于读取和写入文件等任务很有用,在这些任务中可以抛出多种 IOExceptionCallable 也被设计为 运行 在另一个线程上。

Supplier 另一方面,非常一般。只是 "supplies a value" 而已。

所以 CallableSupplier 更专业。如果您不处理另一个线程或者您的任务不太可能抛出异常,建议使用 Supplier

除了显而易见的 Callable 抛出异常之外,区别在于语义。它们有不同的名称,因为它们代表不同的事物。目的是让代码更容易理解。当您使用 Callable 时,您的接口选择意味着该对象将由另一个线程执行。当您使用 Supplier 时,您暗示它只是一个向另一个组件提供数据的对象。

来龙去脉

对于 RunnableCallable,它们自 Java 6 以来一直是并发包的一部分。这意味着它们都已准备好提交给 Executor 和 运行 异步。这里Callable有具体的用法

而对于 Runnable(0 进 0 出)、Supplier(0 进 1 出)、Consumer(1 进 0 出)和 Function(1 in 1 out),自 Java 8 以来,它们一直是功能特性的一部分。所有这些都准备好由 lambda 友好的东西处理,比如 CompletableFuture。这里 Supplier 只是表示一个没有任何输入参数但有一个 return 值的函数,这是高度抽象的。

0 in (arguments) 1 in (argument)
0 out (returned) Runnable Consumer
1 out (returned) Supplier Function

IMO,Callable 和 Supplier 之间的主要区别在于您更喜欢使用已检查异常还是未检查异常。

早些时候,Java 生态系统习惯于为每个预计会遇到的场景选择检查异常。但是 post java-8,checked exceptions 已经不再流行了。 (例如:ExecutionException 与 CompletionException 通常都用于在执行异步任务期间包装异常,但前者被检查而后者未被检查)

这甚至在某些库中也能看到(Jacksons json 解析抛出已检查的异常与 Gson 抛出未经检查的异常等)。

说如果要在另一个线程中执行,就用Callable是不对的。例如,CompletableFuture.supplyAsync(supplier) 在另一个线程中执行供应商。