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.) ?
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<?>
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.
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.
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();
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
T
, SomeGenericType<T>
etc)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 UnsupportedOperationException
s, 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With