Type inference involving return type, wild card, a

2020-05-27 04:52发布

问题:

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.

回答1:

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);
}


回答2:

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



回答3:

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();


标签: java generics