Можно ли вручную завершить CompletableFuture результатом
Также можно завершить с ошибкой:
🟢 Junior Level
Да! Метод complete() позволяет вручную завершить CompletableFuture:
CompletableFuture<String> cf = new CompletableFuture<>();
// Вручную завершить результатом
cf.complete("Hello");
// Теперь cf завершён
System.out.println(cf.isDone()); // true
System.out.println(cf.join()); // Hello
Также можно завершить с ошибкой:
cf.completeExceptionally(new RuntimeException("Error"));
🟡 Middle Level
complete()
CompletableFuture<String> cf = new CompletableFuture<>();
// Завершить
boolean completed = cf.complete("result"); // true
// Повторный complete игнорируется
completed = cf.complete("other"); // false
System.out.println(cf.join()); // "result" — первый побеждает
completeExceptionally()
cf.completeExceptionally(new RuntimeException("Error"));
// При попытке получить результат
try {
cf.join();
} catch (CompletionException e) {
System.out.println(e.getCause()); // RuntimeException: Error
}
obtrudeValue() — принудительное завершение
cf.complete("first");
cf.obtrudeValue("overridden"); // принудительно меняет
System.out.println(cf.join()); // "overridden"
Типичные ошибки
- obtrudeValue без причины:
// ⚠️ obtrudeValue нарушает контракт «однократного завершения» CompletableFuture. // Любой код, который уже получил результат и начал обработку, может работать // с устаревшими данными. Новый результат увидят только те, кто обратится к CF // ПОСЛЕ вызова obtrudeValue. В многопоточной среде — race conditions. cf.obtrudeValue("new value");
🔴 Senior Level
Internal Implementation
public boolean complete(T value) {
boolean triggered = completeValue(value);
if (triggered)
postComplete(); // выполнить все callbacks
return triggered;
}
public void obtrudeValue(T value) {
result = (value == null) ? NIL : value; // force set
}
Production Experience
Manual completion in tests:
@Test
void testManualCompletion() {
CompletableFuture<String> cf = new CompletableFuture<>();
// Имитация async завершения
new Thread(() -> {
Thread.sleep(100);
cf.complete("done");
}).start();
assertEquals("done", cf.get(1, TimeUnit.SECONDS));
}
Best Practices
// ✅ complete() для ручного завершения
cf.complete(result);
// ✅ completeExceptionally() для ошибок
cf.completeExceptionally(error);
// ❌ obtrudeValue() без причины
// ❌ Игнорирование return value complete()
🎯 Шпаргалка для интервью
Обязательно знать:
- complete(value) — завершает CF, если ещё не завершён. Возвращает true/false
- completeExceptionally(ex) — завершает CF с ошибкой
- obtrudeValue(value) — принудительно меняет результат, нарушая контракт однократного завершения
- Первый complete побеждает, повторные игнорируются
- При завершении выполняются все pending callbacks (postComplete)
Частые уточняющие вопросы:
- complete() дважды — что будет? — Первый true, второй false. Результат = первый
- obtrudeValue vs complete? — obtrudeValue force set, даже если CF уже завершён. Dangerous
- completeExceptionally + complete — что будет? — Зависит от тайминга. Кто первый — тот и результат
- Зачем manual completion? — Tests, cache, conditional async, integration с blocking API
Красные флаги (НЕ говорить):
- «complete() можно вызвать несколько раз» — только первый succeeds
- «obtrudeValue — safe для production» — race conditions, нарушает однократное завершение
- «completeExceptionally не выполняет callbacks» — выполняет, как и complete
Связанные темы:
- [[3. Как создать CompletableFuture, который уже завершён с результатом]]
- [[19. Можно ли повторно использовать один CompletableFuture в нескольких цепочках]]
- [[17. Как отменить выполнение CompletableFuture]]
- [[23. Как тестировать код с CompletableFuture]]