Правильно переопределить CompletableFuture.cancel - PullRequest
3 голосов
/ 27 марта 2020

Я заметил, что переопределение метода cancel не действует, если в будущем вызывается thenApply. Производное будущее не знает о том, что оригинал имел такое переопределение. Приведенный ниже код демонстрирует проблему.

Есть ли способ правильно переопределить метод без потери возможности трансформировать будущее?

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
}

Ответы [ 2 ]

3 голосов
/ 30 марта 2020

Вам нужна Java 9 или новее. Затем все методы, создающие новое зависимое будущее, будут вызывать фабричный метод 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(...) с тем же эффектом.

1 голос
/ 27 марта 2020

Нет прямого способа сделать то, что вы хотите, так как thenApply возвращает новый CompletableFuture объект.
Однако, если вам действительно нужно это сделать, вы можете попробовать переопределить сам класс CompletableFuture.

Позвольте мне объяснить! Метод
CompletableFuture.thenApply() далее вызывает закрытый метод uniApplyStage класса CompletableFuture, в котором создается экземпляр new CompletableFuture() для возврата. (Я проверил исходный код Java, чтобы проверить это.)

Вы не можете переопределить этот метод, так как это частный метод. Однако, если вы;

  1. Декомпилируйте класс CompletableFuture
  2. Скопируйте код.
  3. Внесите изменения в метод uniApplyStage в @Override the * Метод 1024 * во время new CompletableFuture<V>() создания экземпляра.
  4. Затем во время выполнения выполните шаги, упомянутые в { ссылка }, чтобы перезагрузить ваш класс во время выполнения с помощью "Custom ClassLoader".

Теоретически, вы должны быть в состоянии сделать все это.

Но, конечно, вопрос остается. Как отчаянно ты хочешь сделать это? : D

По моему честному мнению, считаю этот ответ своего рода последним средством.

...