Я разработал простой интерфейс для этого:
public interface Process<TState, TResult> {
void onProgress(final Consumer<TState> callback);
void onCompletion(final Consumer<TResult> callback);
}
Использование выглядит так:
final Process<Float, Either<IOException, String>> p = download(executor, url);
p.onProgress(progress -> {
System.out.println("Progress: " + progress * 100);
});
p.onComplete(result -> {
System.out.println("Finished: " + result.toString());
});
И общая реализация, которая должна быть поточно-ориентированной:
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public final class SettableProcess<TState, TResult> implements Process<TState, TResult> {
private final Object LOCK = new Object();
private final List<Consumer<TState>> progressCallbacks;
private final List<Consumer<TResult>> completionCallbacks;
private volatile boolean isComplete;
private volatile TResult result;
private SettableProcess() {
progressCallbacks = new ArrayList<>();
completionCallbacks = new ArrayList<>();
isComplete = false;
result = null;
}
@Override
public void onProgress(final Consumer<TState> callback) {
Preconditions.checkNotNull(callback);
if (!isComplete) {
synchronized (LOCK) {
if (!isComplete) {
progressCallbacks.add(callback);
}
}
}
}
@Override
public void onCompletion(final Consumer<TResult> callback) {
Preconditions.checkNotNull(callback);
synchronized (LOCK) {
if (isComplete) {
callback.accept(result);
} else {
completionCallbacks.add(callback);
}
}
}
public void complete(final TResult result) {
Preconditions.checkNotNull(result);
Preconditions.checkState(!isComplete);
synchronized (LOCK) {
Preconditions.checkState(!isComplete);
this.isComplete = true;
this.result = result;
for (final Consumer<TResult> callback : completionCallbacks) {
callback.accept(result);
}
}
completionCallbacks.clear();
progressCallbacks.clear();
}
public void progress(final TState state) {
Preconditions.checkNotNull(state);
Preconditions.checkState(!isComplete);
synchronized (LOCK) {
Preconditions.checkState(!isComplete);
for (final Consumer<TState> callback : progressCallbacks) {
callback.accept(state);
}
}
}
public static <TState, TResult> SettableProcess<TState, TResult> of() {
return new SettableProcess<>();
}
}
Это может быть расширено для поддержки отмены и так далее.