I have an Optional
that I want to \"convert\" to an OptionalInt
, but there doesn\'t seem to be a simple way to do this.
Here\'s what I want
If you have any object and not just a String
, you can temporarily go through a Stream
:
public static <T> OptionalInt toOptionalInt(Optional<T> optional, ToIntFunction<? super T> func) {
return optional.map(Stream::of).orElseGet(Stream::empty)
.mapToInt(func)
.findFirst();
}
This solution has the advantage to be a one-liner, meaning you can copy/paste the content of the method and just change func
to whatever you want. The disadvantage is going through a Stream to achieve what you want. But if you want a generic one-liner, this is it.
If you want a utility method, you probably prefer to use the following:
public static <T> OptionalInt toOptionalInt(Optional<T> optional, ToIntFunction<? super T> func) {
if (optional.isPresent()) {
return OptionalInt.of(func.applyAsInt(optional.get()));
} else {
return OptionalInt.empty();
}
}
This is how I convert an Optional<String>
to OptionalInt
OptionalInt lastSeenId = Optional.of("123").map(Integer::parseInt).map(OptionalInt::of).orElseGet(OptionalInt::empty);
Here's another option that doesn't need to use a Stream and avoids compiling regex every time:
private static final Predicate<String> IS_DIGITS = Pattern.compile("^\\d+$")
.asPredicate();
public OptionalInt getInt() {
return Optional.ofNullable(someString)
.filter(IS_DIGITS)
.map(Integer::valueOf)
.map(OptionalInt::of)
.orElseGet(OptionalInt::empty);
}
Note that you need to anchor the regex because asPredicate() uses find() instead of matches().
Or if you're using Guava, you can eliminate the regex entirely and use their Ints class:
public OptionalInt getInt() {
return Optional.ofNullable(someString)
.map(Ints::tryParse)
.map(OptionalInt::of)
.orElseGet(OptionalInt::empty);
}
While the code isn't more readable than an ordinary conditional expression, there is a simple solution:
public OptionalInt getInt() {
return Stream.of(someString).filter(s -> s != null && s.matches("\\d+"))
.mapToInt(Integer::parseInt).findAny();
}
With Java 9, you could use
public OptionalInt getInt() {
return Stream.ofNullable(someString).filter(s -> s.matches("\\d+"))
.mapToInt(Integer::parseInt).findAny();
}
As said, neither is more readable than an ordinary conditional expression, but I think, it still looks better than using mapOrElseGet
(and the first variant doesn't need Java 9.
No, there's no way to do it in more elegant way using standard Java API. And as far as I know it's not planned to add such methods in JDK-9. I asked Paul Sandoz about adding mapToInt
, etc., here's his answer:
Me:
Isn't it a good idea to provide also a way to transfer between
Optional
types likemapToInt
,mapToObj
, etc., like it's done in Stream API?
Paul:
I don’t wanna go there, my response is transform
Optional*
into a*Stream
. An argument for addingmapOrElseGet
(notice that the primitive variants returnU
) is that other functionality can be composed from it.
So you will likely to have in Java-9:
return Optional.of(someString).filter(s -> s.matches("\\d+"))
.mapOrElseGet(s -> OptionalInt.of(Integer.parseInt(s)), OptionalInt::empty);
But nothing more.
That's because JDK authors insist that the Optional
class and its primitive friends (especially primitive friends) should not be widely used, it's just a convenient way to perform a limited set of operations on the return value of methods which may return "the absence of the value". Also primitive optionals are designed for performance improvement, but actually it's much less significant than with streams, so using Optional<Integer>
is also fine. With Valhalla project (hopefully to arrive in Java-10) you will be able to use Optional<int>
and OptionalInt
will become unnecessary.
In your particular case the better way to do it is using ternary operator:
return someString != null && someString.matches("\\d+") ?
OptionalInt.of(Integer.parseInt(someString)) : OptionalInt.empty();
I assume that you want to return the OptionalInt
from the method. Otherwise it's even more questionable why you would need it.