I'm trying to declare an interface that contains a method which will return a list of things that implement both Comparator<Object>
and Action
, i.e.
<T extends Comparator<Object> & Action> List<T> getThings();
This compiles fine, but the problem comes when I try to call this method. I want to be able to do this:
List<Action> things = getThings();
List<Comparator<Object>> things = getThings();
When I try to do this I get the following compilation error:
incompatible types; no instance(s) of type variable(s) T exist so that
java.util.List<T> conforms to java.util.List<javax.swing.Action>
found : <T>java.util.List<T>
required: java.util.List<javax.swing.Action>
The following doesn't work either:
List<? extends Action> things = getThings();
List<? extends Comparator<Object>> things = getThings();
Another way to achieve this effect is to create an empty interface that extends both Comparator<Object>
and Action
and use that as the return type, i.e.
public interface ComparatorAction extends Comparator<Object>, Action { }
List<ComparatorAction> getThings();
But I don't want to have to do this. There's got to be a way to do what I want, right? Any ideas?
Thanks!
P.S. I'm having a tough time coming up with a good title for this post so feel free to change it.
You could also parameterize the method(s) from which you call getThings()
. For instance:
public static <U extends Comparator<Object> & Action> void main(String[] args) {
List<U> l = getThings();
Action a = l.get(0);
Comparator<Object> c = l.get(0);
}
List<? extends Action> things = getThings();
List<? extends Comparator<Object>> things = getThings();
edit That was my first reaction. Then I thought about it and I didn't think the inference could work, so I deleted the answer.
Checking spec again, they should work. It does compile in JDK7, but fail in JDK6. I guess there's a bug that they have fixed.
edit 2 no... I read the spec(JLS3#15.12.2.8) again, and now I don't think the inference should work. JDK6 was correct in rejecting the inference. (I doubt JDK7 introduced a new bug; it's possible that inference rules are updated, so JDK7 is correct according to new rules. I'm not sure)
According to JLS3, first there is a wildcard capture, a new type parameter W
is introduced, which has upper bound Action
. Then inference has these initial constraints:
List<W> >> List<T>
Comparable >> T
Action >> T
The first constraint yield equality constraint T=W
, and that's it, inference is done.
Now the compiler will check to see if the inferred T
satisfies its bounds, that is, whether
W :< Comparable
W :< Action
The answer is no, the 1st bound cannot be satisfied. (IntelliJ shows a nice error message(better than javac's): "Inferred type '? extends Action' (i.e. W) for type parameter 'T' is not within its bound; should implement Comparable")
edit 3 the question is whether there should be a wildcard capture prior to inference. that's unclear to me. If there shouldn't be, then we have
List<? extends Action> >> List<T>
Comparable >> T
Action >> T
which yields
T :< Action
T :< Comparable
therefore T=Comparable & Action
When you return a List<T>
like that, T
refers to some (unknown) type that is a sub-type of Action
and Comparator<Object>
and is a (preferably the lowest) common super-type of all elements in the list. If such a type does not exist, you are probably going to run into problems.
If you don't care what T
is, you can use a type variable, like Dave Costa suggests, or you can use a wildcard
List<? extends Action> l = getThings();
List<? extends Comparator<Object>> l2 = getThings();