Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 538
Scoobie Avatar asked Sep 01 '16 17:09

Scoobie


People also ask

How do I return a generic class type in Java?

(Yes, this is legal code; see Java Generics: Generic type defined as return type only.) The return type will be inferred from the caller. However, note the @SuppressWarnings annotation: that tells you that this code isn't typesafe. You have to verify it yourself, or you could get ClassCastExceptions at runtime.

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. A solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

How do you return a class in Java?

The getReturnType() method of Method class Every Method has a return type whether it is void, int, double, string or any other datatype. The getReturnType() method of Method class returns a Class object that represent the return type, declared in method at time of creating the method.

How does type erasure work?

Type erasure is a process in which compiler replaces a generic parameter with actual class or bridge method. In type erasure, compiler ensures that no extra classes are created and there is no runtime overhead.


1 Answers

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());
like image 178
vsminkov Avatar answered Sep 23 '22 18:09

vsminkov