Transforms the contents of an optional or stream
Instead of writing my own method or class, I want to check the optional and convert it to another class, or have an empty object.
For this sub-problem in my application, I want to convert the custom user object of a TreeNode instance to a CustomUserObject
, provided that the TreeNode is an instance of DefaultMutableTreeNode
.
private Optional<CustomUserObject> getCustomUserObject(TreeNode node) {
Optional<DefaultMutableTreeNode> optDefaultMutableTreeNode = OptionalUtil.cast(Optional.ofNullable(node), DefaultMutableTreeNode.class);
Optional<Object> optUserObject = optDefaultMutableTreeNode.map(DefaultMutableTreeNode::getUserObject); //
return OptionalUtil.cast(optUserObject, CustomUserObject.class);
}
/**
* Maps the given optional, if the containing element is an instance of the given class.
* Returns empty if the containing object is not an instance of the given class.
*
* @param orgOptional
* given optional
* @param clazz
* given class.
* @return the resulting {@link Optional}.
*/
public static <T, X> Optional<T> cast(Optional<X> orgOptional, Class<T> clazz) {
return orgOptional //
.filter(clazz::isInstance) // check instance
.map(clazz::cast); cast
}
/**
* Maps the given stream, if the containing element is an instance of the given class.
* Returns empty if the containing object is not an instance of the given class.
*
* @param orgStream
* given optional
* @param clazz
* given class.
* @return the resulting {@link Optional}.
*/
public static <T, X> Stream<T> cast(Stream<X> orgStream, Class<T> clazz) {
return orgStream //
.filter(clazz::isInstance) // check instance
.map(clazz::cast); cast
}
I
remember that I often needed to convert optional values or streams in this way. It is not fluent. I actually want java Optional or Stream to have a cast method that performs the above steps. I don’t want to write CustomOptional fluently myself. Am I missing something? Is there an easier way to do this?
Solution
You can easily make it smoother by relying on the map
()/flatMap()
and cast
methods that return the function.
For Optional
, this is easy because map()
can act as a filter by returning null
. So just define:
public static <U> Function<Object, U> filterAndCast(Class<? extends U> clazz) {
return t -> clazz.isInstance(t) ? clazz.cast(t) : null;
}
And use it as:
Optional<Number> number = Optional.of(42L);
System.out.println(number.map(filterAndCast(Integer.class)));
System.out.println(number.map(filterAndCast(Long.class)));
Output:
Optional.empty
Optional[42]
For streams, you can apply more or less the same trick by relying on flatMap()
and functions that return an empty Stream
:
public static <U> Function<Object, Stream<U>> streamFilterAndCast(Class<? extends U> clazz) {
return t -> clazz.isInstance(t) ? Stream.of(clazz.cast(t)) : Stream.empty();
or alternatively
return t -> Stream.of(t).filter(clazz::isInstance).map(clazz::cast);
}
And use it as:
Stream.of(42L, "Hello world", 1024, 3.14)
.flatMap(streamFilterAndCast(Number.class))
.forEach(System.out::println);
Output:
42
1024
3.14