Chaining Optional.orElseThrow - java

I have a piece of code like this:
return getObject()
.map(obj -> obj.getNullableField())
.orElseThrow(() -> new IllegalStateException("Object not found!"));
At the moment I'm throwing an exception when the given Object is not present.
Now I need to also check if the nullableField of Object is present.
One obvious solution could be something like this:
var fieldVal = getObject()
.map(obj -> obj.getNullableField())
.orElseThrow(() -> new IllegalStateException("Object not found!"));
return Optional.ofNullable(fieldVal)
.orElseThrow(() -> new IllegalStateException("Field is not present"));
But I'd like to implement this in the same functional chain...
What am I missing?

It could be implemented within the same chain directly, you would get different exception thrown. Now, it's less readable than your first solution of course, so you have a trade-off.
return getObject().map(obj -> Optional.ofNullable(obj.getNullableField())
.orElseThrow(() -> new IllegalStateException("Field is not present")))
.orElseThrow(() -> new IllegalStateException("Object not found!"));

Rather than nesting, I would suggest a simple sequence to solve that as:
var value = getObject()
.orElseThrow(() -> new IllegalStateException("Object not found!"));
return Optional.of(value) // ensured value check already
.map(CustomObject::getNullableField) // takes care ofNullable
.orElseThrow(() -> new IllegalStateException("Field is not present"));

I'd recommend dropping the Optional approach completly or at least use it only at the beginning (Imagining that getObject() cannot be changed):
var value = getObject()
.orElseThrow(() -> new IllegalStateException("Object not found!"));
var field = value.getNullableField();
if(field == null) {
throw new IllegalStateException("Field is not present");
}
return field;
This doesn't introduce a new Optional wrapper just so you can be "fluent" and do it all in a single line / statement.

Related

Java stream get single element from collection

I am using a non stream way to get single element from collection.
List<MyCustomClass> list = OtherObject.getMyList();
if (list.size() != 1) {
throw new RuntimeException();
}
MyCustomClass customClass = list.get(0);
Instead of this multi liner approach, is there some way to achieve this via streams?
You can use reduce(accumulator) and orElseThrow(exceptionSupplier) to ensure the stream produces exactly one result.
MyCustomClass customClass = list.stream()
.reduce((a,b) -> { throw new RuntimeException("Too many values present"); })
.orElseThrow(() -> { throw new RuntimeException("No value present"); });
I was looking for a version with a single collect statement, although it turned out not as concise or elegant as the solution by Andreas. It uses an implementation of Collector that accumulates to a one-element list, while the combiner raises an exception if we have more than one element; the finisher raises an exception when the list is empty.
list.stream().collect(
Collector.of( ArrayList::new,
(a, t) -> { if (!a.isEmpty())
throw new RuntimeException();
a.add(t); },
(a, b) -> { throw new RuntimeException(); },
a -> { if( a.isEmpty() )
throw new RuntimeException();
return a.get(0);} );
You could try returning an optional from findFirst() or findAny().
List<String> strings = new ArrayList<>();
Optional<String> maybeFirst = strings.stream().findFirst();
// we now have an optional, lets force a value
String value = maybeFirst.orElseThrow(IllegalArgumentException::new);
// if there isn't a value, we'll throw an illegal argument exception.
This can collapsed into the following.
String value = strings.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("There must be at least one string."));
Hope that helps.

Get stream with empty check and feature to use orElse() and orElseThrow() for collections

Optional.ofNullable() checks only for null values and CollectionUtils.isNotEmpty() does not return a stream. Is there a way to combine both these features.Something like this-
Collection.isNotEmpty(entries)
.orElseThrow(() -> new Exception("exception"))
.stream()
Instead of-
Optional.ofNullable(entries)
.orElseThrow(() -> new Exception("exception"))
.stream()
For comparison, consider this:
if (entries == null || entries.isEmpty()) {
throw Exception("exception");
} else {
return entries.stream();
}
(Holger has mentioned pretty much the same thing in a couple comments.)
In my opinion, using Optional for this case isn't an improvement over a conventional if/else statement.
You can simply use filter() to check that it's not empty
Optional.ofNullable(entries)
.filter(e -> !e.isEmpty())
.orElseThrow(() -> new Exception("exception"))
.stream()
Regarding your comment that you want to eliminate null values in the stream itself you can use this:
Optional.ofNullable(entries)
.filter(e -> !e.isEmpty())
.orElseThrow(() -> new Exception("exception"))
.stream()
.filter(Objects::nonNull)
Perhaps:
Optional.ofNullable((entries == null || entries.isEmpty()) ? null : entries)
.orElseThrow(() -> new Exception("exception"))
.stream()
You can map to a stream as:
Optional.ofNullable(entries)
.filter(a -> !a.isEmpty())
.orElseThrow(() -> new Exception("exception"))
// do whatever with the stream if available

Java 8 Map: filter value and throw exception if match found

I'm trying to find a matching value in a Map and if found, I need to throw an IllegalArgumentException. My code is as follows:
final String stringToBeMatched = "someRandomString";
map.values()
.stream()
.filter(a -> stringToBeMatched == a.getField())
.findAny()
.ifPresent(a -> throw new IllegalArgumentException());
I get a syntax error on token "throw". I'm not sure where I'm going wrong.
A lambda body can be an expression or a block of statements. However,
throw new IllegalArgumentException()
is a statement, which is neither. Make it a block by surrounding it with braces.
.ifPresent(a -> {throw new IllegalArgumentException(); } );
And for your next question, compare your string values with .equals, not with ==.
Alternate cleaner solution, using anyMatch:
boolean found = map.values()
.stream()
.anyMatch(a -> stringToBeMatched.equals(a.getField()));
if(found) {
throw new IllegalArgumentException();
}
findAny return a Optional, you can use orElseThrow
map.values()
.stream()
.filter(a -> stringToBeMatched == a.getField())
.findAny()
.orElseThrow(() -> new IllegalArgumentException());

Java streams .orElseThrow

I want convert a piece of code from a Connection Pool project i have been working on to use streams
the original code is
for (Map.Entry<JdbConnection,Instant> entry : borrowed.entrySet()) {
Instant leaseTime = entry.getValue();
JdbConnection jdbConnection = entry.getKey();
Duration timeElapsed = Duration.between(leaseTime, Instant.now());
if (timeElapsed.toMillis() > leaseTimeInMillis) {
//expired, let's close it and remove it from the map
jdbConnection.close();
borrowed.remove(jdbConnection);
//create a new one, mark it as borrowed and give it to the client
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
}
}
throw new ConnectionPoolException("No connections available");
I have got to the point of this
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now()).toMillis() > leaseTimeInMillis)
.findFirst()
.ifPresent(entry -> {
entry.getKey().close();
borrowed.remove(entry.getKey());
});
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
The above can compile but the moment i add orElseThrow after IfPresent i am getting the following
/home/prakashs/connection_pool/src/main/java/com/spakai/ConnectionPool.java:83: error: void cannot be dereferenced
.orElseThrow(ConnectionPoolException::new);
That's because ifPresent returns void. It can't be chained. You could do something like:
Entry<JdbConnection, Instant> entry =
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now())
.toMillis() > leaseTimeInMillis)
.findFirst()
.orElseThrow(ConnectionPoolException::new));
entry.getKey().close();
borrowed.remove(entry.getKey());
What you were looking for would read well:
.findFirst().ifPresent(value -> use(value)).orElseThrow(Exception::new);
But for it to work, ifPresent would have to return the Optional, which would be a little odd. It would mean you could chain one ifPresent after another, doing multiple operations on the value. That might have been a good design, but it isn't the one the creators of Optional went with.
Use map instead of isPresent, and return with an Optional instead of an exception.
borrowed.entrySet().stream()
.filter(entry -> Duration.between(entry.getValue(), Instant.now()).toMillis() > leaseTimeInMillis)
.findFirst()
.map(entry -> {
entry.getKey().close();
borrowed.remove(entry.getKey());
JdbConnection newJdbConnection = factory.create();
borrowed.put(newJdbConnection,Instant.now());
return newJdbConnection;
})

Throw an exception if an Optional<> is present

Let's say I want to see if an object exists in a stream and if it is not present, throw an Exception. One way I could do that would be using the orElseThrow method:
List<String> values = new ArrayList<>();
values.add("one");
//values.add("two"); // exception thrown
values.add("three");
String two = values.stream()
.filter(s -> s.equals("two"))
.findAny()
.orElseThrow(() -> new RuntimeException("not found"));
What about in the reverse? If I want to throw an exception if any match is found:
String two = values.stream()
.filter(s -> s.equals("two"))
.findAny()
.ifPresentThrow(() -> new RuntimeException("not found"));
I could just store the Optional, and do the isPresent check after:
Optional<String> two = values.stream()
.filter(s -> s.equals("two"))
.findAny();
if (two.isPresent()) {
throw new RuntimeException("not found");
}
Is there any way to achieve this ifPresentThrow sort of behavior? Is trying to do throw in this way a bad practice?
You could use the ifPresent() call to throw an exception if your filter finds anything:
values.stream()
.filter("two"::equals)
.findAny()
.ifPresent(s -> {
throw new RuntimeException("found");
});
Since you only care if a match was found, not what was actually found, you can use anyMatch for this, and you don't need to use Optional at all:
if (values.stream().anyMatch(s -> s.equals("two"))) {
throw new RuntimeException("two was found");
}
userOptional.ifPresent(user1 -> {throw new AlreadyExistsException("Email already exist");});
Here middle bracket is compulsory, else it is showing compile time exception
{throw new AlreadyExistsException("Email already exist");}
public class AlreadyExistsException extends RuntimeException
and exception class must extends runtime exception

Categories

Resources