Почему существует разница между сборщиками coroutine для CompletableFuture и ListenableFuture?

Изучая источник сопрограмм Kotlin, я заметил разницу (обозначенную ** ) между JDK 8 CompletableFuture

 public fun <T> future( context: CoroutineContext = DefaultDispatcher, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): CompletableFuture<T> { require(!start.isLazy) { "$start start is not supported" } val newContext = newCoroutineContext(context) val job = Job(newContext[Job]) val future = CompletableFutureCoroutine<T>(newContext + job) job.cancelFutureOnCompletion(future) ** future.whenComplete { _, exception -> job.cancel(exception) } ** start(block, receiver=future, completion=future) // use the specified start strategy return future } private class CompletableFutureCoroutine<T>( override val context: CoroutineContext ) : CompletableFuture<T>(), Continuation<T>, CoroutineScope { override val coroutineContext: CoroutineContext get() = context override val isActive: Boolean get() = context[Job]!!.isActive override fun resume(value: T) { complete(value) } override fun resumeWithException(exception: Throwable) { completeExceptionally(exception) } ** doesn't override cancel which corresponds to interrupt task ** } 

и Гуава ListenableFuture

 public fun <T> future( context: CoroutineContext = DefaultDispatcher, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): ListenableFuture<T> { require(!start.isLazy) { "$start start is not supported" } val newContext = newCoroutineContext(context) val job = Job(newContext[Job]) val future = ListenableFutureCoroutine<T>(newContext + job) job.cancelFutureOnCompletion(future) start(block, receiver=future, completion=future) // use the specified start strategy return future } private class ListenableFutureCoroutine<T>( override val context: CoroutineContext ) : AbstractFuture<T>(), Continuation<T>, CoroutineScope { override val coroutineContext: CoroutineContext get() = context override val isActive: Boolean get() = context[Job]!!.isActive override fun resume(value: T) { set(value) } override fun resumeWithException(exception: Throwable) { setException(exception) } ** override fun interruptTask() { context[Job]!!.cancel() } ** } 

хотя я думаю о типах как почти эквивалентных (за исключением, конечно, ListenableFuture не может быть завершен напрямую, но я не понимаю, почему это имеет значение здесь). Есть ли конкретная причина этой разницы?

CompletedFuture.cancel – открытый (переопределяемый) метод, но он не предназначен для переопределения . Его документация не дает никаких гарантий для ее обращения при аннулировании, поэтому единственный способ гарантировать будущее (каламбур), чтобы узнать, что CompletableFuture был отменен, – это установить на него слушателяComplete.

Например, совершенно очевидно, что для будущей версии JDK будет добавлен еще один способ отменить будущее, которое не будет внутренне вызывать cancel . Такое изменение не будет нарушать какой-либо контракт CompletableFuture .

Сравните это с документацией по AbstractFuture.interruptTask . Этот метод явно разработан для переопределения, и его документация гарантирует условия, при которых он вызывается. Таким образом, мы можем обеспечить несколько более эффективную реализацию для ListenableFuture строителя, которая позволяет избежать создания лямбды, чтобы установить на нее прослушиватель отмены.