Чи можна вручну завершити 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]]