13

I have the following situation:

public ArrayList<A> getMethods(){
    return b.c.test();
}

So, my problem is that b.c.test() returns a value with Optional<A> as return type. But I need to return an ArrayList<A>.

So, I tried to cast it and rewrite it to :

public ArrayList<A> getMethods(){
    return (ArrayList<A>)b.c.test();
}

But Eclipse says that such a cast from Optional<A> to ArrayList<A> is not possible.

How can I solve this problem?

dimo414
  • 47,227
  • 18
  • 148
  • 244
user3097712
  • 1,565
  • 6
  • 27
  • 49

8 Answers8

30

I am presuming your intended semantic is 'if the value is present return a list with a single item, otherwise return an empty list.' In that case I would suggest something like the following:

ArrayList<A> result = new ArrayList<>();
b.c.test().ifPresent(result::add);
return result;

However I would suggest your return type should be List<A> rather than ArrayList<A> as that gives you the opportunity to change the type of list without changing the callers. It would also allow you to return Collections.EMPTY_LIST if the optional value is not present which is more efficient than creating an unnecessary ArrayList.

Update: there's now an easier option with Java 9:

b.c.test().stream().collect(Collectors.toList());

Update: and even easier option with Java 16:

b.c.test().stream().toList();
sprinter
  • 27,148
  • 6
  • 47
  • 78
26

In this case, it is possible to not use streams at all:

public static <T> List<T> toList(Optional<T> opt) {
    return opt.isPresent()
            ? Collections.singletonList(opt.get())
            : Collections.emptyList();
}

Or, the same code using the functional API:

public static <T> List<T> toList(Optional<T> opt) {
    return opt
            .map(Collections::singletonList)
            .orElseGet(Collections::emptyList);
}

I prefer the upper variant because I know for sure that it doesn't create any unnecessary objects on the Java heap.

Roland Illig
  • 261
  • 3
  • 3
7

If everyone insists on using streams for this issue, it should be more idiomatic than using ifPresent() Unfortunately, Java 8 does not have a Optional.stream() method, so it is not possible to do:

 optional.stream().collect(Collectors.toList());

see also: Using Java 8's Optional with Stream::flatMap

But in JDK 9, it will be added (and that code actually already runs on Java 9)

Optional<Integer> o = Optional.empty();
final List<Integer> list = o.stream().collect(Collectors.toList());
System.out.println(list);
Community
  • 1
  • 1
user140547
  • 7,750
  • 3
  • 28
  • 80
2
return b.c.test()
    .map(Arrays::asList).map(ArrayList::new)
    .orElseGet(ArrayList::new);

If the optional has a value, it "maps" it to a List<A> with Arrays.asList and then to an ArrayList via the new ArrayList<A>(List<A>) constructor; otherwise it yields an empty ArrayList via the empty constructor.

This could be more explicitly written out as:

return b.c.test()
    .map(value -> new ArrayList<A>(Arrays.asList(value)))
    .orElseGet(() -> new ArrayList<A>());
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • This seems excessively verbose; isn't this actually misusing Java 8's functional idioms for a very simple task? – Mick Mnemonic Jul 28 '15 at 23:13
  • 1
    I dunno, I like this style. I like the lack of temporary variables. I see the complexity as a consequence of the overly specific return type. If it were `List` instead of `ArrayList` this could be written more straightforwardly: `map(Collections::singletonList).orElseGet(Collections::emptyList)`. I like the way that looks. – John Kugelman Jul 28 '15 at 23:35
  • IMO, the temporary variable in @sprinter's answer makes the code more readable (and much more concise). `map` and `orElseGet` are useful -- when used in the right place. Your solution just reminded me of the caveats presented in [Guava's Functionals Explained](https://code.google.com/p/guava-libraries/wiki/FunctionalExplained) -- imperative code should be the default, even in Java 8. – Mick Mnemonic Jul 29 '15 at 00:10
2

With Java9, you can do this using the newly added Optional::stream API :

Optional<List<A>> list;
List<A> collect = list.stream()
               .flatMap(Optional::stream)
               .collect(Collectors.toList());
Niraj Sonawane
  • 10,225
  • 10
  • 75
  • 104
1

Since Java 9

return b.c.test().stream()
                 .collect(toList());

import static java.util.stream.Collectors.toList;

Since Java 8

return b.c.test()
           .map(List::of)
           .orElse(emptyList());

import static java.util.Collections.emptyList;

epox
  • 9,236
  • 1
  • 55
  • 38
0

An Optional is a container object which may or may not contain a non-null value.

In ArrayList terms I would translate it as an array which has 0 or 1 members.

public ArrayList<A> getMethods(){
  Optional<A> opt = b.c.test();
  ArrayList<A> res = new ArrayList<>();
  if ( opt.isPresent() )
    res.add( opt.get() );
  return res;
}
Anonymous Coward
  • 3,140
  • 22
  • 39
  • [`Optional.empty()`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#empty--) is a static method that returns a new, empty `Optional`. You want [`Optional.isPresent()`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#isPresent--). There are also cleaner ways to do this (@sprinter's answer). – dimo414 Jul 28 '15 at 23:00
  • @JoseAntonioDuraOlmos I think you mean `if (opt.isPresent())` rather than `if (!opt.isPresent())`. At the moment this will throw an exception when you try to `get` and empty optional. – sprinter Jul 28 '15 at 23:26
  • Of course, that is what happens for changing blindly !opt.empty() for !opt.isPresent(). – Anonymous Coward Jul 28 '15 at 23:31
0

If you're using Guava's Optional, you can do:

return new ArrayList<>(b.c.test().asSet());

This will extract the value from the Optional (if present) and add it to a new ArrayList.

If you're using Java 8, @sprinter's answer is what you need.

dimo414
  • 47,227
  • 18
  • 148
  • 244