Home > Mobile >  How to convert Optional<List<Integer>> to Optional<ArrayList<Integer>>
How to convert Optional<List<Integer>> to Optional<ArrayList<Integer>>

Time:07-25

I've tried to do the following, but it doesn't work

Optional<List<Integer>> listOne = someAPI.call()
// convert
Optional<ArrayList<Integer>> listTwo = new ArrayList<>(listOne); // doesn't work
Optional<ArrayList<Integer>> listTwo = Optional.of(new ArrayList(listOne)); // also not working

Note that the first API returns Optional<List>. And I need to send Optional<ArrayList> to another API.

CodePudding user response:

You can convert it like this:

Optional<ArrayList<Integer>> listTwo = listOne.map(ArrayList::new);

CodePudding user response:

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 lack.

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.

CodePudding user response:

If you start with this:

Optional<List<Integer>> optionalList = getFromSomewhere();

You can convert to Optional<ArrayList<Integer>> like this:

Optional<ArrayList<Integer>> optionalArrayList
        = Optional.of(new ArrayList<>(optionalList.get()));

This is close to what you tried - Optional.of(new ArrayList(listOne)) – but your attempt didn't work because listOne is type Optional<List<Integer>>, whereas the constructor to new ArrayList() takes a Collection. If you call .get() then you'll get the list out of the Optional, and that works fine for the ArrayList constructor.

UPDATE: This is a safer way to do it, guarding empty optional. Agree that the other answer – map(ArrayList::new) – is a simpler, cleaner way to do it. In this answer, I wanted to address the approach initially taken by OP, how it was close and could be made to work.

if (list.isPresent()) {
    Optional<ArrayList<Integer>> optionalArrayList = Optional.of(new ArrayList<>(optionalList.get()));
}
  • Related