Handle checked exception in lambda [duplicate] - java

This question already has answers here:
Java 8 Lambda function that throws exception?
(27 answers)
Closed 3 years ago.
I have some code that throws a checked exception. I want to call that code within a lambda in order to create a map from another map:
Map<String, Coordinate> map = getMap();
Map<String, Integer> result = map.entrySet().stream().collect(
toMap(x -> x.getKey(), x -> doSomething(x.getValue)));
where doSometing is the code that throws the exception:
int doSomething(Coordinate c) throws MyException { ... }
Now compiler surely complains about the exception not being handled. So I surround it with a try-catch, which looks pretty ugly:
Map<String, Integer> result = map.entrySet().stream().collect(
toMap(x -> x.getKey(), x -> {
try {
return doSomething(x.getValue());
} catch (MyException e) {
e.printStackTrace();
// return some error-code here???
}
}));
which also does not compile as we need to return something in the catch-case. However there´s not much sense in returning anything here in this exceptional case, which is why I actually do not want to handle the exception at that level. Can´t I just handle the exception in my calling code, where I create the lambda? So to say just one level above?
try {
Map<String, Integer> result = ...
} catch (MyException e) { ... }
But that does not compile because the exception thrown from the lambda is not handled.

From Baeldung's blog: you could define consumer which can throw Exception:
#FunctionalInterface
public interface ThrowingConsumer<T, E extends Exception> {
void accept(T t) throws E;
}
and a static wrapper to map checked exception to RuntimeException:
static <T> Consumer<T> throwingConsumerWrapper(
ThrowingConsumer<T, Exception> throwingConsumer) {
return i -> {
try {
throwingConsumer.accept(i);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
};
}
Then you can call it:
Map<String, Integer> result = map.entrySet().stream()
.collect(
throwingConsumerWrapper(toMap(x -> x.getKey(), x -> doSomething(x.getValue)))
);

Fist of all it's worth to filter your values. In case if you have values which can raise an exception.
Then you can use one of the popular wrappers (i.e. jooq.lambda) or write your own
map.entrySet().stream()
.filter(x -> makeSureNoExtection(x))
.collect(toMap(x -> x.getKey(), unchecked(x -> doSomething(x.getValue))));

Related

How to handle exceptions thrown in streams map without try catch?

I want to handle the exception without using try catch clauses.
void test() {
list.stream().map(obj -> {
Batch batch = x.findByBatchId(obj.getBatchId())
/// exception is thrown here
.orElseThrow(() -> new ResourceNotFoundException(""));
if (obj.getBatchStatus() != null) {
batchHelper.updateBatchStatus(batch, obj.getBatchStatus());
}
});
}
You can't throw checked exception that way.
You can:
Throw RuntimeException and handle it later, maybe in another method, or don't handle it at all:
public void test() {
list.stream().map(obj -> {
Batch batch = x.findByBatchId(obj.getBatchId())
/// exception is thrown here
.orElseThrow(() -> new RuntimeException(""));
if (obj.getBatchStatus() != null) {
batchHelper.updateBatchStatus(batch, obj.getBatchStatus());
}
});
Use for statement and add your exception to method signature:
public void test() throws ResourceNotFoundException {
for (BatchInfo bi: list) {
.findByBatchId(bi.getBatchId()).orElseThrow(
() -> new ResourceNotFoundException(""));
}
It also depends on what you want to do in case of exception.
If you just want to let users know they do something wrong - use RuntimeException
If you want just update Batch status, use forEach, not map :
list.stream().forEach(obj -> {
Batch batch = x.findByBatchId(obj.getBatchId())
/// exception is thrown here
.orElseThrow(() -> new RuntimeException(""));
if (obj.getBatchStatus() != null) {
batchHelper.updateBatchStatus(batch, obj.getBatchStatus());
}
});

Bad return type in method reference: Cannot convert Employee to Optional<U>

I am trying to write a lambda function that gets employee location preference and have the code sample below.
But for my lambda function I get a compilation error at flatMap(this::buildEmployeeGeolocation)
saying Bad return type in method reference: cannot convert com.abc.EmployeeGeolocation to java.util.Optional<U>.
What am I missing here?
public Optional<EmployeeGeolocation> getEmployee(final SessionId sessionId) {
return Optional.ofNullable(employeePreferencesStore.getEmployeeAccountPreferences(sessionId))
.map(preferences -> preferences.getPreference(PreferenceKey.Location))
.filter(StringUtils::isNotBlank)
.map(this::readEmployeelocation)
.flatMap(this::buildEmployeeGeolocation);
}
private Optional<EncryptedGeolocation> readEmployeeLocation(#NonNull final String encryptedGeolocation) {
try {
return Optional.ofNullable(objectMapper.readValue(encryptedGeolocation, EmployeeGeolocation.class));
} catch (final IOException e) {
log.error("Error while reading the encrypted geolocation");
throw new RuntimeException(e);
}
}
private EmployeeGeolocation buildEmployeeGeolocation(#NonNull final EncryptedGeolocation unditheredEncryptedGeolocation) {
return EmployeeGeolocation.builder()
.latitude(10.0)
.longitude(10.0)
.accuracy(1.0)
.locationType(ADDRESS)
.build();
}
It seems like what you really need to do is swap the map and flatMap. Change the code
.map(this::readEmployeeLocation)
.flatMap(this::buildEmployeeGeolocation);
to
.flatMap(this::readEmployeeLocation) // since you already have an Optional<String>
.map(this::buildEmployeeGeolocation); // above results in Optional<EncryptedGeolocation>
Important: Inferred from the code Optional.ofNullable(...).map(...).filter(StringUtils::isNotBlank), that it would result in an Optional<String> until this operation.

Java 8 stream attaching error handling for later consumption

Let's say I have the following method I want to refactor
protected Stream<T> parseFile(File file, Consumer<File> cleanup) {
try {
return parser.parse(file); // returns a Stream<T>
} catch (XmlParseException e) { // child of RuntimeException
throw new CustomRuntimeException(e);
} finally {
if (file != null) {
cleanup.accept(file);
}
}
throw new IllegalStateException("Should not happen");
}
This method's purpose is to act as a proxy attaching error handling on the stream rethrowing in a wrapping exception CustomRuntimeException. So when we consume it later in the flow, I don't have to handle those exceptions everywhere but only CustomRuntimeException.
Upstream, I used that method as follow
try {
Stream<T> stream = parseFile(someFile);
stream.map(t -> ...);
catch (CustomRuntimeException e) {
// do some stuff
}
And here's what the parser.parse method looks like
public Stream<T> parse() {
// ValueIterator<T> implements Iterator<T>, AutoCloseable
XmlRootParser.ValueIterator<T> valueIterator = new XmlRootParser.ValueIterator(this.nodeConverter, this.reader, this.nodeLocalName, this.nodeName);
Stream<T> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(valueIterator, 1040), false);
stream.onClose(valueIterator::close);
return stream;
}
The exceptions I want to handle will be thrown by the ValueIterator.hasNext method. Which means they won't be thrown at Stream creation but only at Stream consumption (calling foreach/map/count/collect/... on the stream).
How do I attach error handling on my stream in method parseFile nicely without having to consume the stream? Is it possible?
Obviously this code will work only if the parser.parse method consume its stream before returning it. Which is against using streams.
The Stream’s backend which provides the iterator logic, is the Spliterator.
So you can wrap the element processing using a wrapper Spliterator like this:
class Wrapper<T> implements Spliterator<T> {
final Spliterator<T> source;
public Wrapper(Spliterator<T> source) {
this.source = source;
}
#Override
public boolean tryAdvance(Consumer<? super T> action) {
try {
return source.tryAdvance(action);
}
catch(XmlParseException ex) {
throw new CustomRuntimeException(ex);
}
}
#Override
public void forEachRemaining(Consumer<? super T> action) {
try {
source.forEachRemaining(action);
}
catch(XmlParseException ex) {
throw new CustomRuntimeException(ex);
}
}
#Override public Spliterator<T> trySplit() {
Spliterator<T> srcPrefix = source.trySplit();
return srcPrefix == null? null: new Wrapper<>(srcPrefix);
}
#Override public long estimateSize() { return source.estimateSize(); }
#Override public int characteristics() { return source.characteristics(); }
#Override public Comparator<? super T> getComparator(){return source.getComparator();}
}
It retains all properties of the original Spliterator and only translates exceptions thrown during the iteration.
Then you can use it like
protected Stream<T> parseFile(File file) {
Stream<T> s = parser.parse();
return StreamSupport.stream(new Wrapper<>(s.spliterator()), s.isParallel())
.onClose(s::close);
}
And the caller should not forget to close the stream properly:
ResultType result;
try(Stream<T> s = parseFile(file)) {
result = s.
// other intermediate ops
// terminal operation
}
or
ResultType result;
try(Stream<T> s = parseFile(file)) {
result = s.
// other intermediate ops
// terminal operation
}
finally {
// other cleanup actions
}
You could use helper stream initialization class that handles the process of preparing stream and catch any exception there. Consider following example:
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SafeInitializationStreamExample {
public static void main(String[] args) {
int sum = SafeInitializationStream.from(() -> Stream.of(1,2,3,4))
.onInitializationError(t -> System.out.println(t.getMessage()))
.mapToInt(it -> it)
.sum();
System.out.println(sum);
List<Object> list = SafeInitializationStream.from(() -> parse("/tmp/test.log"))
.onInitializationError(t -> System.out.println(t.getMessage()))
.map(it -> it.toString())
.collect(Collectors.toList());
System.out.println(list);
}
private static <T> Stream<T> parse(String filename) {
throw new RuntimeException("File does not exist!");
}
static class SafeInitializationStream<T> {
private final Supplier<Stream<T>> streamSupplier;
private SafeInitializationStream(Supplier<Stream<T>> streamSupplier) {
this.streamSupplier = streamSupplier;
}
public static <T> SafeInitializationStream<T> from(Supplier<Stream<T>> streamSupplier) {
return new SafeInitializationStream<>(streamSupplier);
}
public Stream<T> onInitializationError(Consumer<Throwable> onError) {
try {
return streamSupplier.get();
} catch (Exception e) {
onError.accept(e);
}
return Stream.empty();
}
}
}
In this example we introduce SafeInitializationStream class which expects a Supplier<Stream<T>>:
SafeInitializationStream.from(() -> Stream.of(1,2,3,4))
Using Supplier<Stream<T>> in this case makes stream initialization lazy - until we call Supplier.get() the body of this supplier is not executed. Now, when we call:
.onInitializationError(t -> System.out.println(t.getMessage()))
we execute suppliers body, catch any exception that may be thrown and handle this exception by passing Throwable to Consumer<Throwable> that was passed as a parameter to onInitializationError method. In case of exception Stream.empty() is returned (so you can safely apply all other transformations in chain. When there is no exception, Stream<T> provided by supplier is returned.
If you run following example you will get in console:
10
File does not exist!
[]
The first stream was consumed without any errors and the sum was returned correctly.
The second stream thrown exception during initialization, we have caught it and printed to the console and finally empty list was returned after consuming the stream.
Of course you can use Function<Throwable, Stream<T>> in onInitializationError method if you want to specify what Stream<T> is returned in case of an exception. In this case we assume that Stream.empty() is always returned in this case. Hope it helps.

Handling exceptions while returning values by Optional flatmap

I have tried implementing Optional from JAVA in the below code. getOrders() method throws a KException.
#Override
public Optional<List<Order>> getPendingOrders(AuthDTO authDTO) throws MyException {
List<Order> orders=null;
try {
Optional<KConnect> connection = connector.getConnection(authDTO);
if (connection.isPresent()) {
orders = connection.get().getOrders();
}
}catch (KException e){
throw new MyException(e.message,e.code);
}
return Optional.ofNullable(orders);
}
I tried to remove the isPresent() check and replace it with flatmap.
#Override
public Optional<List<Order>> getPendingOrders(AuthDTO authDTO) throws MyException {
return connector.getConnection(authDTO).map(p -> {
try {
return p.getOrders();
} catch (KException e) {
throw new MyException(e.message,e.code); // [EXP LINE]
}
});
}
In this case I am not able to catch KException and convert it to MyException. My code won't compile.
unreported exception com.myproject.exception.MyException; must be caught or declared to be thrown
1st snippet works perfectly fine for me. Is there a better way to do this? Please suggest.
Edit: This does not change if I make it map instead of flatmap.
The exact problem is that IntelliJ is saying unhandled exception: com.myproject.exception.MyException at [EXP LINE] even though the method has throws MyExpception present.

java.lang.IllegalStateException: Iterator already obtained

I modified this code to do several tasks in one directory:
public class HDDSerialNumber
{
public void getHDDSerialNumber() throws IOException
{
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD Model
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/model")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
// Get HDD Vendor
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/vendor")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
// Get HDD Vendor
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/state")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
}
static <T, R> Function<T, R> wrap(IOFunction<T, R> f)
{
return t ->
{
try
{
return f.apply(t);
}
catch (IOException ex)
{
throw new UncheckedIOException(ex);
}
};
}
interface IOFunction<T, R>
{
R apply(T in) throws IOException;
}
}
But when I run the code I get this error stack:
run:
ST320LT012-9WS14
Exception in thread "main" java.lang.IllegalStateException: Iterator already obtained
at sun.nio.fs.UnixDirectoryStream.iterator(UnixDirectoryStream.java:118)
at sun.nio.fs.UnixSecureDirectoryStream.iterator(UnixSecureDirectoryStream.java:73)
at java.lang.Iterable.spliterator(Iterable.java:101)
at hardware.HDDSerialNumber.getHDDSerialNumber(HDDSerialNumber.java:25)
at hardware.Hardware.main(Hardware.java:12)
Java Result: 1
Can you help me to fix the code, please? I suppose that Iterator already obtained must be used only once in this example but I don't have idea how to fix this.
While DirectoryStream extends Iterable, it is not a general-purpose Iterable as it supports only a single Iterator; invoking the iterator method to obtain a second or subsequent iterator throws IllegalStateException.
(Source)
The iterator of the Iterable returned by Files.newDirectoryStream (DirectoryStream implements Iterable) can only be used once. You can solve it by calling Files.newDirectoryStream separately for each of the 3 streams you are creating.
Instead of creating one DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"); and using it in all 3 StreamSupport.stream calls, create 3 DirectoryStream<Path>.
Example :
public void getHDDSerialNumber() throws IOException
{
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD Model
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/model")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD Vendor
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/vendor")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD State
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/state")).flatMap(wrap(Files::lines))
.forEach(System.out::println);
}
}
EDIT :
If you want to handle the case of a file that doesn't exist without interrupting the program execution, catch the exception thrown in that case.
For example :
try (DirectoryStream<Path> ds = Files.newDirectoryStream(Paths.get("/sys/block"), "sd*"))
{
// Get HDD State
StreamSupport.stream(ds.spliterator(), false)
.map(p -> p.resolve("device/state"))
.flatMap(wrap(path - > try {
return Files.lines(path);
} catch (IOException ioEx) {
return Stream.empty();
}))
.forEach(System.out::println);
}
This would catch the exception and return an empty Stream.

Categories

Resources