Callable
和Supplier
是Java的两个函数式接口,分别位于java.util.concurrent
和java.util.function
包中,具有以下签名-
public interface Callable<V> {
V call() throws Exception;
}
public interface Supplier<T> {
T get();
}
有没有一些特定的使用情况,其中一个比另一个更合适?
Callable
和Supplier
是Java的两个函数式接口,分别位于java.util.concurrent
和java.util.function
包中,具有以下签名-
public interface Callable<V> {
V call() throws Exception;
}
public interface Supplier<T> {
T get();
}
从各自的文档中可以看出它们的使用差异:
一个返回结果并且可能抛出异常的任务。实现者定义了一个名为call的无参方法。
Callable接口类似于Runnable,两者都是为其实例有可能由另一个线程执行的类而设计。
表示提供结果的供应商。
没有要求每次调用供应商时都返回新的或不同的结果。
这意味着调用者期望在调用Callable.call
时会抛出异常,并相应地处理异常。这对于像读写文件这样的任务非常有用,因为可能会抛出许多种IOException
。 Callable
也设计为在另一个线程上运行。
另一方面,Supplier
非常通用。它只是“提供一个值”而已。
因此,Callable
比Supplier
更加专业化。如果您没有涉及到其他线程,或者您的任务很少可能抛出异常,则建议使用Supplier
。
Runnable
和Callable
自Java 6以来一直是并发包的一部分。这意味着它们都可以被提交给一个Executor
异步运行。在这里,Callable
有一个特定的用法。
而Runnable
(0输入0输出)、Supplier
(0输入1输出)、Consumer
(1输入0输出)和Function
(1输入1输出)都是自Java 8以来函数特性的一部分。它们都可以被像CompletableFuture
这样的lambda友好的东西处理。这里的Supplier
只是表示没有任何输入参数但具有返回值的函数,高度抽象。
除了Callable抛出异常这一明显的区别外,两者的差异在于语义上。它们有不同的名称,因为它们代表着不同的事物。目的是使代码更易于理解。当您使用Callable时,您的接口选择意味着该对象将由另一个线程执行。当您使用Supplier时,您意味着它仅是向另一个组件提供数据的对象。
在我看来,Callable和Supplier的主要区别在于您更喜欢使用已检查的异常还是未检查的异常。
早期,Java生态系统通常会对预期遇到的每种情况都使用已检查的异常。但自从Java8之后,已检查的异常在流行度方面下降了。(例如:在异步任务执行期间包装异常时,ExecutionException与CompletionException通常都用于包装异常,但前者是已检查的,而后者则不是)
这甚至在一些库中也能看到(例如:Jackson的JSON解析会抛出已检查的异常,而Gson则会抛出未检查的异常等)。
不能说如果将在另一个线程中执行就使用Callable是正确的。例如,CompletableFuture.supplyAsync(supplier)
会在另一个线程中执行提供程序。
call
会抛出异常,而supply
则不会。 (与Supplier
相比) - Glains