5

I was looking at the Java Generics documentation and found this piece of code,

public class WildcardError {

void foo(List<?> l) {
    //This give a compile time error
    l.set(0,l.get(0));
}
}

I can understand that we are fetching an element from a List<?> and trying to set it to another List<?>. So the compiler gives an error. My question is it makes sense when the 2 lists are different i.e. l.set(0, m.get(0)) here lists l and m are different. But in the above example, l and l are the same lists. Why isn't the compiler smart enough to see that? Is it hard to implement it?

Edit: I am aware that I can fix it by a helper method or by using T instead of a ?. Just wondering why compiler doesn't do it for me.

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
vikky.rk
  • 3,989
  • 5
  • 29
  • 32

3 Answers3

9

In your specific case, you can explicitly fix this:

public class WildcardError {
    <T> void foo(List<T> l) {
        // This will work
        l.set(0, l.get(0));
    }
}

Or if you don't want to change the original API, introduce a delegate helper method:

public class WildcardError {
    void foo(List<?> l) {
        foo0(l);
    }

    private <T> void foo0(List<T> l) {
        // This will work
        l.set(0, l.get(0));
    }
}

Unfortunately, the compiler cannot infer that "obvious" <T> type. I've been wondering about that, too. It seems like something that could be improved in a compiler, as every wild card can be informally translated to an unknown <T> type. Probably, there are some reasons why this was omitted, perhaps this is only intuition, but formally impossible.

UPDATE:

Note, I've just seen this peculiar implementation of Collections.swap():

public static void swap(List<?> list, int i, int j) {
    final List l = list;
    l.set(i, l.set(j, l.get(i)));
}

The JDK guys resort to a raw type, in order to handle this, locally. This is a strong statement indicating that this probably should be supported by the compiler, but for some reason (e.g. lack of time to formally specify this) just wasn't done

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • 1
    Yes I am aware of the other alternatives to fix this. Just wondering why compiler can't do that by itself when both lists are the same. – vikky.rk Jul 16 '12 at 07:52
  • 1
    Yes, I'm wondering about that too. It would've kept me from writing hundreds of useless delegation methods in the past... :-/ Maybe you should update your question and ask for a specific explanation from the [JLS](http://docs.oracle.com/javase/specs/jls/se7/html/index.html), which explains this. – Lukas Eder Jul 16 '12 at 07:54
  • Thanks for editing the post :). Where do I get to ask the JLS? – vikky.rk Jul 16 '12 at 08:07
  • @vikky.rk: I've re-phrased your question [here](http://stackoverflow.com/questions/11500385/how-does-the-jls-justify-that), to get a more formal answer. Usually, if you're wondering about Java language features, it helps to mention the JLS and request formal explanations – Lukas Eder Jul 16 '12 at 08:14
  • Thanks. So with the 'JLS' tag, do you expect anyone from JLS to reply? Sorry I am new to SO, so just understanding how it works. – vikky.rk Jul 16 '12 at 08:20
  • @vikky.rk: No problem, it takes a bit of practice on SO, to rule out answers that are correct but not what you intended :-) Yours is already pretty good. You will not get answers from Joshua Bloch or Brian Goetz directly, of course. But the more precise your question is, the more precise the answers will be... – Lukas Eder Jul 16 '12 at 08:22
  • Oh ok. I am new to Java, just exploring the language. So was not sure how to phrase the question correctly. – vikky.rk Jul 16 '12 at 08:28
4

The compiler reports an error because there is no way -- in general -- that it can tell whether two expressions, (in this case l and l) refer to the same list.

Related, somewhat generalized, question:

Community
  • 1
  • 1
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • I've been wondering about a formal reason for this for a long time. I personally feel that this is a lacking feature in the JLS, as the two wild cards in both `l` are formally the same. At least by intuition... But probably, in more complex pieces of code, it would get harder to recognise "identical" wild cards – Lukas Eder Jul 16 '12 at 07:53
  • Yeah. There are probably some interesting corner cases which would force the spec to be quite complex. Good question though. It's kind of a generalization of this question. You should post it as a follow up. – aioobe Jul 16 '12 at 07:55
  • 1
    OK, this is the question: http://stackoverflow.com/questions/11500385/how-does-the-jls-justify-that-wildcards-cannot-be-formally-used-within-methods – Lukas Eder Jul 16 '12 at 08:22
  • Yea. I saw it when you posted it. +1. – aioobe Jul 16 '12 at 08:56
  • Check my updated answer. I just discoverd a method in the JDK that "suffers" from the same problem... The JDK people resorted to raw types, to circumvent this problem. – Lukas Eder Aug 16 '12 at 07:30
4

List<?> means list containing elements of some unknown type, so when one wants to take elements from it using list.get(i) it will return object of some unknown type, so the only valid guess will be Object. Then when one tries to set element back using list.set(index, list.get(index)) it produces compile-time error, since as mentioned above List<?> can only contain some unknown type, so putting Object to it may cause ClassCastException.

This is explained very well in Joshua Bloch's Effective Java, 2nd ed., Item 28: Use bounded wildcards to increase API flexibility

This is also known as PECS principle and good explanation can be found in this Q/A: What is PECS (Producer Extends Consumer Super)? (please note that List<?> is the same as List<? extends Object> with minor exceptions)

In laymans terms, one should use List<?> as method parameter only to get elements from it inside that method, not when one needs to put elements into list. When one needs to both put and get he/she needs to either generify method using type parameter T as in Lukas Eder's answer (type-safe way) or simply use List<Object> (not type-safe way).

Community
  • 1
  • 1
Yuriy Nakonechnyy
  • 3,742
  • 4
  • 29
  • 41