Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple wildcard bounds

Suppose that I have the following class:

public class Either<A, B> {
    public Object get();
}

Either is a type that stores one object of either type A or B. get() retrieves that one object.

The question is whether or not it is possible to use generics to alter the method signature of get() so that the type returned is not just Object, but any common supertype of A and B. For example, an Either<Integer, Long> can have get() return Number, an Either<Deque<T>, Set<T>> can have get() return Iterable<T> or Collection<T>, and so on. (Obviously, an Either<Foo,Foo> should have get() return Foo).

If this is at all possible, if I had Either<List<A>, List<B>>, what is the most specific type get() can return? Is it raw List, wildcard List<?>, or something else entirely?

like image 405
Kelvin Chung Avatar asked Sep 01 '12 17:09

Kelvin Chung


4 Answers

Java inference do have something similar, we can do

public static <C, A extends C, B extends C> C get(Either<A,B> e)
{   return (C)e.get();    }

inference:

    A=Integer, B=Long    ==>    C=Number

    A=List<Integer>, B=List<Long>      ==>   C=List<? extends Number>

usage:

    Either<Integer, Long> x = ...;
    get(x); // the return type is Number

However there's probably no way to turn it into an instance method. We would need to write

public class Either<A,B>

    public <C super A|B> C get() { ... }

or simply

    public A|B get(){ ... }

which is not supported in Java

like image 114
irreputable Avatar answered Nov 10 '22 10:11

irreputable


Why not define an abstract class C, containing as much logic common to A and B as you deem necessary, and refer to that in your Either class:

public class Either<C> {
    public C get();
}

That doesn't seem like much of an answer, but since Java erases your type information anyway when compiling (that it to say, your compiled code sees only Object instead of an A or a B), then you are in the best position to define what should be retained in an explicit common class.

like image 23
davek Avatar answered Nov 10 '22 09:11

davek


As far as I know, it is not possible: your Either<A,B> class makes an assumption about a generic third type (let's call it C), which both A and B would extend: while it is possible to write something like public class Either<A extends MyNonFinalClass, B extends MyNonFinalClass> {}, Java doesn't allow forward-referencing a generic type, so you cannot even write something like Either<A extends C, B extends C, C>. Clearly a shame, as your Either class would really be handy :)

like image 1
Guillaume Avatar answered Nov 10 '22 08:11

Guillaume


You need to make sure A and B share a common ancestry.

public class Either<A extends CommonAncestor, B extends CommonAncestor> {
    public CommonAncestor get() {....}
}

or

public class Either<C, A extends C, B extends C> {
    public C get() {....}
}
like image 1
MadProgrammer Avatar answered Nov 10 '22 08:11

MadProgrammer