I know how to create a reference to a method that has a String
parameter and returns an int
, it\'s:
Function
Create a custom return type that will propagate the checked exception. This is an alternative to creating a new interface that mirrors the existing functional interface with the slight modification of a "throws exception" on the functional interface's method.
public static interface CheckedValueSupplier {
public V get () throws Exception;
}
public class CheckedValue {
private final V v;
private final Optional 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 getException () {
return opt;
}
public static CheckedValue returns (T t) {
return new CheckedValue(t);
}
public static CheckedValue rethrows (Exception e) {
return new CheckedValue(e);
}
public static CheckedValue from (CheckedValueSupplier sup) {
try {
return CheckedValue.returns(sup.get());
} catch (Exception e) {
return Result.rethrows(e);
}
}
public static CheckedValue escalates (CheckedValueSupplier 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 sToFr =
(fn) -> new FileReader(Paths.get(fn).toFile());
// Alternative, this will compile.
Function> 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 paths = ...; //a list of paths to files
Collection> 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();
A single functional interface that throws a checked exception is created (CheckedValueSupplier
). This will be the only functional interface which allows checked exceptions. All other functional interfaces will leverage the CheckedValueSupplier
to wrap any code that throws a checked exception.
The CheckedValue
class will hold the result of executing any logic that throws a checked exception. This prevents propagation of a checked exception until the point at which code attempts to access the value that an instance of CheckedValue
contains.
CheckedValue#get()
is called.Some functional interfaces (Consumer
for example) must be handled in a different manner as they don't provide a return value.
One approach is to use a function instead of a consumer, which applies when handling streams.
List 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
Alternatively, you can always escalate to a RuntimeException
. There are other answers that cover escalation of a checked exception from within a Consumer
.
Just avoid functional interfaces all together and use a good-ole-fashioned for loop.