在Java中,我理解了EDCOX1 0和EDCX1·1接口的区别。从Java 1.5向EDCOX1·0接口添加了额外的特性,并被称为EDCOX1×1,以保持向后兼容性。
我的问题是,现在我们有了Callable接口,我们应该一直使用它吗?不使用Callable和使用Runnable的用例是什么?
(这篇文章很好地说明了他们之间的区别)
- 在阅读了相关问题的答案后,还有什么实际问题?需要可调用的好处。或者他们不是。
- @用户2246674可调用的好处是生动的。我看不出在手上有了callable之后再使用runnable有什么意义。你呢?
- 我愿意。可能有一些代码需要可运行的,在这种情况下,可调用的代码是不够的。对象下既不可调用也不可运行unify。另外,为什么使用Callable不需要Callable的特性?如果没有返回值-返回null类型化为对象怎么办?
- @用户2246674确定当您被迫使用runnable时,您必须。但是当您可以自由地在runnable和callable之间进行选择时,使用runnable没有任何意义,是吗?
- 两者都不应该比另一个更"首选"…在给定上下文(或给定库方法的参数类型)的情况下,使用任何有意义的方法。
- 不好的建议。如果您不受向后兼容性的限制,那么Callable绝对是您的首选。换句话说,绝对喜欢可调用的,而不是传统的可运行的。
- @里奇-为什么?如果您不需要Callable的特性,为什么要使用它?
- @sheidaei-鉴于runnable是(稍微)简单的接口,我的第一反应是使用runnable,除非您需要Callable提供的一些好处。所以我会把你的问题转过来:当你不需要时,使用Callable有什么意义吗?
两者都有各自的用途,并且都由java.util.concurrent中的Executor框架支持。runnable已经存在了更长的时间,但它仍然在使用中,并不气馁。
可调用的对象可以抛出异常并返回值,这使得它们更好地抽象了结果承载任务(例如从网络中获取资源、执行一个昂贵的计算值等)(在Java实践中,Goetz,布洛赫等人,Java并发的标准工作)。
因此,如果您正在设计API,我建议尽可能使用可调用文件。如果您确定任务不会返回值,也不会引发异常,那么runnables也是一个有效的选择。这里没有黑色和白色,特别是因为可运行文件可以很容易地用可调用文件包装,反之亦然。
另外,请注意,可调用的实现不需要声明throws Exception;事实上,可调用本身声明它只是允许实现者抛出任何选中的异常。但是,仅依赖可调用接口的可调用调用对象必须编写异常处理代码。还要注意,可调用项不需要返回值;您只需声明可调用项返回Void(大写'V)。
imho,runnable是一个更好的类型,当把一个函数作为参数使用时,
- 没有返回值,但只有副作用
- 必须处理异常,而不是传播它们
别忘了Callable.call()抛出异常。这意味着,如果您将一个可调用项作为参数,这个可调用项可以抛出任何类型的异常,并且您必须有一种正确的方法来处理它们。如果您不能做到这一点,最好让可调用的实现者按自己的意愿处理异常,并使参数成为可运行的,以使其明确。
不使用Callable的用例:ScheduledExecutorService.scheduleAtFixedRate和scheduleWithFixedDelay只接受Runnable。
- 好,简洁的可运行用例!
- 以前的代码强制使用runnable的案例已经被很好地覆盖了。
我更喜欢Callables,但在那些罕见的情况下,您可能需要一个Callable作为Runnable,您可以通过向您的Callable中添加这样的run()方法轻松实现这两种方法。
1 2 3 4 5 6 7
| public void run (){
try{
call ();
} catch (Exception e ){
e. printStackTrace(); // or however you log errors
}
} |
在Java 8中,你也可以为你做一个界面来做同样的事情:
1 2 3 4 5 6 7 8 9
| public interface CallableRunnable <T > extends Callable <T >, Runnable {
default void run (){
try {
call ();
} catch (Exception e ) {
e. printStackTrace(); // or however you log errors
}
}
} |
然后你只要把implements Callable改成implements CallableRunnable就可以了,这样,你的工作就可以被任何需要的方法调用。当然,如果您需要特定的错误处理,您仍然可以重写run()方法来处理call()方法抛出的异常。您甚至可以实现一个方法来实现这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface CallableRunnable <T > extends Callable <T >, Runnable {
default void run (){
try {
call ();
} catch (Exception e ) {
handleCallExceptions (e );
}
}
default void handleCallExceptions (Exception e ){
e. printStackTrace();
}
} |
那么任何特殊的异常处理只需要实现自己的handleExceptions(Exception)方法…但如果你不需要的话,你就不必这么做。我喜欢这样,因为它允许您使用日志框架等实现。
可调用和可运行两者都很相似,可以用于实现线程。在实现runnable的情况下,必须实现run()方法,但在可调用的情况下,必须实现call()方法,这两种方法的工作方式相似,但call()方法的灵活性更高,两者之间存在一定的差异。
可运行和可调用的区别如下--
1)runnable的run()方法返回void,意味着如果您希望线程返回一些可以进一步使用的东西,那么对于runnable run()方法您没有选择。有一个"可调用"的解决方案,如果您想以对象的形式返回任何东西,那么您应该使用可调用而不是可运行。可调用接口具有返回对象的方法"call()"。
方法签名-可运行的>
可调用的>
2)对于runnable run()方法,如果出现任何选中的异常,则必须使用try catch块进行处理,但是对于callable call()方法,可以按如下方式引发选中的异常
3)RunnFabess来自遗留Java 1版本,但可调用的Java 1.5版本采用ExcUnter框架。
如果您熟悉执行器,那么应该使用Callable而不是Runnable。
希望你能理解。