正确覆盖 CompletableFuture.cancel

Correctly override CompletableFuture.cancel

我注意到,如果在未来调用 thenApply,则重写 cancel 方法不会生效。派生的未来并不知道原始版本有这样的覆盖这一事实。下面的代码演示了这个问题。

有没有什么方法可以正确覆盖方法而不失去改造未来的可能性?

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args){
        CompletableFuture<Object> unmodified = new CompletableFuture<>(){
            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                System.out.println("----> cancelling unmodified");
                return super.cancel(mayInterruptIfRunning);
            }
        };
        unmodified
                .cancel(true);


        CompletableFuture<Object> modified = new CompletableFuture<>(){
            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                System.out.println("----> cancelling modified");
                return super.cancel(mayInterruptIfRunning);
            }
        };
        unmodified
                .thenApply(x -> x)
                .cancel(true);
        System.out.println("----> end");
    }
    // OUTPUT
    // > Task :Main.main()
    // ----> cancelling unmodified
    // ----> end
}

没有直接的方法做你想做的事,因为 thenApply returns 一个新的 CompletableFuture 对象。
但是,如果您真的需要这样做,您可以尝试覆盖 CompletableFuture class 本身。

让我解释一下!
CompletableFuture.thenApply() 方法进一步调用 CompletableFuture class 的 uniApplyStage 私有方法,其中创建 new CompletableFuture() 实例以返回。 (我反编译了 Java 源代码来检查这一点。)

您不能覆盖此方法,因为它是私有方法。但是,如果你;

  1. 反编译 class CompletableFuture
  2. 复制代码。
  3. new CompletableFuture<V>() 创建实例时将 uniApplyStage 方法更改为 @Override cancel 方法。
  4. 然后在运行时,按照 中提到的步骤使用 "Custom ClassLoader".
  5. 在运行时重新加载您的 class

理论上,你应该能够做到这一切。

当然,问题仍然存在。你有多拼命想要完成这件事? :D

老实说,将这个答案视为最后的手段。

您需要 Java 9 或更高版本。然后,所有创建新的依赖 future 的方法都将调用工厂方法 newIncompleteFuture(),您可以覆盖它:

public static void main(String arg[]) {
    class MyFuture<T> extends CompletableFuture<T> {
        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            System.out.println("customized cancel");
            return super.cancel(mayInterruptIfRunning);
        }

        @Override
        public <U> CompletableFuture<U> newIncompleteFuture() {
            return new MyFuture<>();
        }
    }
    MyFuture<Object> future = new MyFuture<>();

    System.out.print("direct: ");
    future.cancel(true);

    System.out.print("indirect: ");
    future.thenApply(x -> x).cancel(true);

    System.out.print("even longer chain: ");
    future.thenApply(x -> x).exceptionally(t -> null).cancel(true);

    System.out.println("end");
}
direct: customized cancel
indirect: customized cancel
even longer chain: customized cancel
end

但请记住,对于 CompletableFuture,取消与 CancellationException 的异常完成没有什么不同。所以,有人可以调用 completeExceptionally(new CancellationException()) 而不是 cancel(...),达到同样的效果。