简体   繁体   中英

Given `T` and `U` where `T extends U` how to return a `U`

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 a Bar

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM