Создайте пользовательский тип возвращаемого значения, который будет распространять проверенное исключение. Это альтернатива созданию нового интерфейса, который отражает существующий функциональный интерфейс с небольшой модификацией «исключения исключения» в методе функционального интерфейса.
Определение
CheckedValueSupplier
public static interface CheckedValueSupplier<V> {
public V get () throws Exception;
}
CheckedValue
public class CheckedValue<V> {
private final V v;
private final Optional<Exception> opt;
public Value (V v) {
this.v = v;
}
public Value (Exception e) {
this.opt = Optional.of(e);
}
public V get () throws Exception {
if (opt.isPresent()) {
throw opt.get();
}
return v;
}
public Optional<Exception> getException () {
return opt;
}
public static <T> CheckedValue<T> returns (T t) {
return new CheckedValue<T>(t);
}
public static <T> CheckedValue<T> rethrows (Exception e) {
return new CheckedValue<T>(e);
}
public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
return Result.rethrows(e);
}
}
public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Применение
// Don't use this pattern with FileReader, it's meant to be an
// example. FileReader is a Closeable resource and as such should
// be managed in a try-with-resources block or in another safe
// manner that will make sure it is closed properly.
// This will not compile as the FileReader constructor throws
// an IOException.
Function<String, FileReader> sToFr =
(fn) -> new FileReader(Paths.get(fn).toFile());
// Alternative, this will compile.
Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
return CheckedValue.from (
() -> new FileReader(Paths.get("/home/" + f).toFile()));
};
// Single record usage
// The call to get() will propagate the checked exception if it exists.
FileReader readMe = pToFr.apply("/home/README").get();
// List of records usage
List<String> paths = ...; //a list of paths to files
Collection<CheckedValue<FileReader>> frs =
paths.stream().map(pToFr).collect(Collectors.toList());
// Find out if creation of a file reader failed.
boolean anyErrors = frs.stream()
.filter(f -> f.getException().isPresent())
.findAny().isPresent();
В чем дело?
Создается единственный функциональный интерфейс, который выдает проверенное исключение ( CheckedValueSupplier
). Это будет единственный функциональный интерфейс, который позволяет проверять исключения. Все остальные функциональные интерфейсы будут использоватьCheckedValueSupplier
для переноса любой код, который выдает проверенное исключение.
CheckedValue
Класс будет содержать результат выполнения какой - либо логики, генерирующий проверяемое исключение. Это предотвращает распространение проверенного исключения до точки, в которой код пытается получить доступ к значению, которое экземплярCheckedValue
.
Проблемы с этим подходом.
- Теперь мы выкидываем «Исключение», эффективно скрывая определенный тип первоначально брошенного.
- Мы не знаем, что произошло исключение, пока не
CheckedValue#get()
будет вызвано.
Потребитель и др.
Некоторые функциональные интерфейсы (Consumer
например) должны обрабатываться другим способом, поскольку они не обеспечивают возвращаемое значение.
Функция вместо потребителя
Один из подходов заключается в использовании функции вместо потребителя, которая применяется при обработке потоков.
List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
.map(e -> CheckedValueSupplier.from(
() -> {throwyMethod(e); return e;}))
.filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior
обострять
Кроме того, вы всегда можете перейти к RuntimeException
. Существуют и другие ответы, которые охватывают эскалацию проверенного исключения изнутри a Consumer
.
Не потреблять
Просто избегайте функциональных интерфейсов и используйте петлю for в хорошем стиле.