Java 8 Optional asSet()

2019-05-01 12:38发布

问题:

So I have been using Guava's Optional for a while now, and I moving to Java 8 so I wanted to use it's Optional class, but it doesn't have my favorite method from Guava, asSet(). Is there a way to do this with the Java 8 Optional that I am not seeing. I love being able to treat optional as a collection so I can do this:

for( final User u : getUserOptional().asSet() ) {
   return u.isPermitted(getPermissionRequired());
}

In some cases avoids the need for an additional variable.

IE

 Optional<User> optUser = getUserOptional();
 if ( optUser.isPresent() ) {
     return optUser.get().isPermitted(getPermissionRequired());
 }

Is there an easy way to replicate the Guava style in Java 8's Optional?

Thanks

回答1:

There is a simple way of converting an Optional into a Set. It works just like any other conversion of an Optional:

Given an Optional<T> o you can invoke

o.map(Collections::singleton).orElse(Collections.emptySet())

to get a Set<T>. If you don’t like the idea of Collections.emptySet() being called in every case you can turn it into a lazy evaluation:

o.map(Collections::singleton).orElseGet(Collections::emptySet)

however, the method is too trivial to make a performance difference. So it’s just a matter of coding style.

You can also use it to iterate like intended:

for(T t: o.map(Collections::singleton).orElse(Collections.emptySet()))
    // do something with t, may include a return statement


回答2:

You appear to only be using asSet so you can write a for loop, but that's unnecessary in Java 8. Instead of your code

Optional<User> optUser = getUserOptional();
if ( optUser.isPresent() ) {
    return optUser.get().isPermitted(getPermissionRequired());
}

you could write

getUserPresent().map(optUser -> optUser.isPermitted(getPermissionRequired()))
   .orElse(false);

...or, in many cases, you could use Optional.ifPresent(Consumer<T>).



回答3:

You can use map :

return optUser.map(u -> u.isPermitted(getPermissionRequired()));

But it would return an Optional<WhateverTypeIsPermittedReturns>.

Reference

public Optional map(Function mapper)

If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result. Otherwise return an empty Optional.



回答4:

I also don't see a really elegant, built-in way to do this, but would like to propose a solution similar to that of Dici:

public static <T> Set<T> asSet(Optional<T> opt) {
    return opt.isPresent() ?
        Collections.singletonSet(opt.get()) :
        Collections.emptySet();
}

This avoids the creation of a new HashSet instance and the insertion of the optional object. (Note: This is basically the "inlined" version of what Guavas Present and Absent classes do: The Present class returns the singletonSet, and the Absent class returns the emptySet).

The usage would be similar to your original pattern:

for( final User u : asSet(getUserOptional()) ) {
   return u.isPermitted(getPermissionRequired());
}


回答5:

You can modify your getUserOptional so that it returns a Set or implement your own asSet method :

public Set<User> getUserOptional() {
     Optional<User> optUser;
     //your code
     Set<User> result = new HashSet<>();
     if (optUser.isPresent())
         result.add(optUser.get());
     return result;
}

or

public static <T> Set<T> asSet(Optional<T> opt) {
    Set<T> result = new HashSet<>();
    if (opt.isPresent())
        result.add(opt.get());
    return result;
}

I don't know if there is a built-in way to do it however.