Given an API like:
class Bar { ... }
class Foo extends Bar { ... }
In Java's Optional
type, we can say:
Optional<Foo> fooOption = ...
fooOption.orElse(aFoo) // returns something of type Foo
But, since Foo
is a Bar
, I would like to be able to say:
Optional<Foo> fooOption = ...
fooOption.orElse(aBar) // returns something of type Bar
As an exercise, I wanted to accomplish this with another type:
public abstract class Option<T> {
// this doesn't compile
public abstract <U super T> U orElse(U other);
}
How would I rewrite this to compile, but also support the ability to widen the type when desired at the same time?
But, since
Foo
is aBar
But Bar
is not a Foo
. What I mean is that you can do this:
Optional<Bar> fooOpt = Optional.of(new Foo());
Bar bar = fooOpt.orElse(new Bar());
But you can't do the same thing with Optional<Foo>
because it violates type constraints of Optional.orElse
method.
In hypothetical implementation of Option<T>
you should explicitly define U
as a supertype of T
public class Option<U, T extends U> {
T value;
public U orElse(U other) {
if (value != null) {
return value;
}
return other;
}
}
In that case you could wrote a code like this
Option<Foo, Bar> fooOpt = Option.of(new Foo());
Bar bar = fooOpt.orElse(new Bar());
Since U
should be the super-type of T
, you could do:
public abstract class Option<U, T extends U> {
public abstract of(T value);
public abstract U orElse(U other);
}
You can define your own method using map
to expand the type:
public static <U, T extends U> U orElse(Optional<T> tOpt, U u) {
return tOpt.<U>map(Function.identity()).orElse(u);
}
There is a basic premise that is flawed in your question:
In Java's Optional type, we can say:
Optional fooOption = ... fooOption.orElse(aFoo) // returns something of type Foo But, since Foo is a Bar, I would like to be able to say:
Optional fooOption = ... fooOption.orElse(aBar) // returns something of type Bar
A Foo is a Bar, but a Bar is not a Foo. If your Generic is defined as this:
Optional<Foo> fooOption = ...
Then you can return anything that is of type Foo
. A Bar
is not of type Foo
.
If you had an additional object:
class FooBar extends Foo{}
Then you could cast it to a foo in your example:
Optional fooOption = ...
fooOption.orElse(aFooBar) // returns something of type Foo
Or, optionally, if you had defined your Optional
as Optional<Bar>
then you could use either Foo
or Bar
or FooBar
objects, since they are all of or inherit from type Bar
.
As to the second part of your question:
public abstract class Option<T> {
// this doesn't compile
public abstract <U super T> U orElse(U other);
}
Just by writing this with the common supertype:
public abstract class Option<T> {
// this now compiles.
public abstract T orElse(T other);
}
You're saying that anything that is of or inherits from type T is acceptable.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.