By making use of Optional<ArrayList<Integer>>
you piggyback one contrived problem on top of the other.
Don't wrap Collections with Optional
Any Collection
by itself could represent the absence of data by being empty. And you can safely interact with an empty collection. Wrapping it with an Optional
is redundant.
Here is a quote from the answer by Brian Goetz, Java language Architect, regarding how the Optional
is intended to be used, and how it shouldn't be used from the perspective of its creators:
Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null
for such was overwhelmingly likely to cause errors.
For example, you probably should never use it for something that
returns an array of results, or a list of results; instead return an
empty array or list.
Conclusion: design of the first API returning Optional<List<Integer>>
is incorrect.
Don't use Optional as an Argument
This considered a misuse of the Optional because it goes against the main design goal of Optional (see the quote above, also have a look at the answer by Stuart Marks, Java and OpenJDK developer).
Conclusion: design of the second API expecting Optional<ArrayList<Integer>>
is also wrong.
Write your code against Interfaces
Leverage abstractions, write your code against interfaces. There's no justification for making the code inflexible and capable only of consuming instances of ArrayList
because it would not be able to deal with a List
produced by Collections.emptyList()
, List.of()
, Arrays.asList()
, Stream.toList()
, etc.
See What does it mean to "program to an interface"?
Fixing the problem
Fix both endpoints if you can. Make them work without Optional
.
In case if you're not in control of this first API returning the optional, fine, then don't make the problem bigger - fix the second API, make it expect a list of integer. And unpack the optional result right on the spot before call the second API.
You can use orElse()
for that purpose:
List<Integer> list = someAPI.call().orElse(Collections.emptyList());
If you can't change both APIs, then you're out of luck.
Also, not that listOne.map(ArrayList::new)
creates a copy of the initial data, that means that your system instead of making the second call immediately is forced to perform an additional step because of poor API design.