По предложению @ amon вот более монадический ответ. Это очень упрощенная версия, в которой вы должны принять несколько предположений:
функция "unit" или "return" является конструктором класса
операция связывания происходит во время компиляции, поэтому она скрыта от вызова
функции "action" также связаны с классом во время компиляции
хотя класс является универсальным и охватывает любой произвольный класс E, я думаю, что в данном случае это на самом деле избыточно. Но я оставил это как пример того, что вы могли бы сделать.
Исходя из этих соображений, монада превращается в плавный класс-обертку (хотя вы теряете большую гибкость, которую получаете в чисто функциональном языке):
public class RepositoryLookup<E> {
private String source;
private E answer;
private Exception exception;
public RepositoryLookup<E>(String source) {
this.source = source;
}
public RepositoryLookup<E> fetchElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookup(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public RepositoryLookup<E> orFetchSimilarElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookupVariation(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public RepositoryLookup<E> orFetchParentElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookupParent(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public boolean failed() {
return exception != null;
}
public Exception getException() {
return exception;
}
public E getAnswer() {
// better to check failed() explicitly ;)
if (this.exception != null) {
throw new IllegalArgumentException(exception);
}
// TODO: add a null check here?
return answer;
}
}
(это не скомпилируется ... некоторые детали остаются незавершенными, чтобы сохранить размер выборки)
И вызов будет выглядеть так:
Repository<String> repository = new Repository<String>(x);
repository.fetchElement().orFetchParentElement().orFetchSimilarElement();
if (repository.failed()) {
throw new IllegalArgumentException(repository.getException());
}
System.err.println("Got " + repository.getAnswer());
Обратите внимание, что у вас есть возможность составлять операции «извлечения» так, как вам нравится. Он остановится, когда получит ответ или исключение, отличное от не найденного.
Я сделал это очень быстро; это не совсем правильно, но, надеюсь, передает идею
NotFoundException
то, что на самом деле является исключительным?