Java Generics Capture List<?>

前端 未结 3 1724
孤独总比滥情好
孤独总比滥情好 2021-01-13 11:48

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

public class WildcardError {

void foo(List l) {
    //This give a co         


        
相关标签:
3条回答
  • 2021-01-13 12:01

    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

    0 讨论(0)
  • 2021-01-13 12:15

    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).

    0 讨论(0)
  • 2021-01-13 12:17

    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:

    • How does the JLS specify that wildcards cannot be formally used within methods?
    0 讨论(0)
提交回复
热议问题