Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upcast object contained in Java 8 Optional?

Is there an efficient way to perform upcasting while using an Optional object. Here is a sample code:

class A{}
class B extends A{}

B func(){
    //do something
    return new B();
}

Optional<B> func2(){
    //do something
    return Optional.of(new B());
}

main() {
    A a = func(); // Upcasting works fine
    B b = func(); // Upcasting works fine
    Optional<B> b = func2(); // 1. Works fine
    Optional<A> a = func2(); // 2. How to make this work ?
}

(2.) gives an error. I can solve it by creating another function.

But is there an efficient way, so that func2() can be used for both (1.) and (2.) ?

like image 974
DoOrDoNot Avatar asked Jun 30 '17 18:06

DoOrDoNot


People also ask

How do you type optional in Java?

Casting the value of an Optional or the elements of a Stream is a two-step-process: First we have to filter out instances of the wrong type, then we can cast to the desired one. With the methods on Class , we do this with method references. Using the example of Optional : Optional<?>

How do I retrieve an object from optional?

Once you have created an Optional object, you can use the isPresent() method to check if it contains a non-null value. If it does, you can use the get() method to retrieve the value. Developers can also use the getOrElse() method, which will return the value if it is present, or a default value if it is not.


4 Answers

The cleanest way to do this I think would be Optional<A> a = func2().flatMap(Optional::of) or Optional<A> a = func2().map(Function.identity()) because both do not require casting and only use existing functionality.

like image 140
Deckard Avatar answered Oct 10 '22 22:10

Deckard


Optional is, quote from java doc:

a container object which may or may not contain a non-null value.... Additional methods that depend on the presence or absence of a contained, such as orElse() ...

As we can see Optional container, uses generic type to define which object to contain.

Optional <T>

so you can do this:

Optional<? extends A> a = func2(); // 2. Upcasting Will work :)

and to access the optional object a an object of type A :

a.get();
like image 44
Mr.Q Avatar answered Oct 17 '22 11:10

Mr.Q


I would write a method like this:

@SuppressWarnings("unchecked")  // Safe. See below.
static <T> Optional<T> copyOf(Optional<? extends T> opt) {
  return (Optional<T>) opt;
}

(If you don't like the name copyOf, see my comment about Guava's ImmutableList below)

This is very efficient in terms of runtime speed: the cast gets elided at compile time:

static <T> java.util.Optional<T> copyOf(java.util.Optional<? extends T>);
    Code:
       0: aload_0  # Read the parameter.
       1: areturn  # Return the parameter.

so the only cost is that of a method call; this is easily done away with by the JIT.

You can then invoke like:

Optional<A> a = copyOf(func2());

This is safe because Optional has the following property: it is guaranteed not to have any state changes caused by setter methods taking parameters dependent upon the type variable T. Phew. Quite a mouthful. I'll make it more concrete.

Because Optional

  1. has no setter methods (of any kind, but more generally none that take parameters of type T, SomeGenericType<T> etc)
  2. is final (so you can't subclass it to add a setter to violate the previous point)

there is nothing you can do to the value held by the Optional<T> (or lack thereof) that will make it not an instance of T (or lack thereof).

And because every instance of T is also an instance of its superclasses, there is nothing unsafe about:

SuperclassOfT s = optionalOfT.get();

As such, this method is type safe (it will fail if you've invoked it on a non-present optional; but that's not a type error).

You will find similar code in Guava's ImmutableList.copyOf (the inspiration for calling it "copyOf" above, even though it's not really a copy). There, there are setter methods (like add), but those methods immediately throw UnsupportedOperationExceptions, and thus do not affect the list's state.


Note that whilst immutable types have the necessary properties described above to make such a cast safe, the type does not necessarily need to be immutable to perform the cast safely.

For example, you could have an ErasableOptional<T> type, which has an erase() method on it which, when called, converted a "present" value into an "absent" value (i.e. get() no longer succeeds). It would be safe to cast such an instance to an ErasableOptional<SupertypeOfT> because the value is either a T or absent; you can't make it not an instance of SupertypeOfT or absent.

like image 8
Andy Turner Avatar answered Oct 17 '22 12:10

Andy Turner


You can try using a mapping function:

Optional<A> oa = func2().map(v -> (A) v);

Note that you don't really need to put the (A) cast there, but it makes it a little clearer what's going on.

like image 6
Treyzania Avatar answered Oct 17 '22 13:10

Treyzania