Generic method with parameters vs. non-generic method with wildcards

前端 未结 4 1655
执笔经年
执笔经年 2021-02-07 05:15

According to this entry in the Java Generics FAQ, there are some circumstances where a generic method has no equivalent non-generic method that uses wildcard types. According

相关标签:
4条回答
  • 2021-02-07 05:47

    Well, going by the spec, neither invocation is legal. But why does the first one type check while the second does not?

    The difference is in how the methods are checked for applicability (see §15.12.2 and §15.12.2.2 in particular).

    • For simple, non-generic g to be applicable, the argument Class<ArrayList> would need to be a subtype of Class<? extends Iterable<?>>. That means ? extends Iterable<?> needs to contain ArrayList, written ArrayList <= ? extends Iterable<?>. Rules 4 and 1 can be applied transitively, so that ArrayList needs to be a subtype of Iterable<?>.

      Going by §4.10.2 any parameterization C<...> is a (direct) subtype of the raw type C. So ArrayList<?> is a subtype of ArrayList, but not the other way around. Transitively, ArrayList is not a subtype of Iterable<?>.

      Thus g is not applicable.

    • f is generic, for simplicity let us assume the type argument ArrayList is explicitly specified. To test f for applicability, Class<ArrayList> needs to be a subtype of Class<T> [T=ArrayList] = Class<ArrayList>. Since subtyping is reflexisve, that is true.

      Also for f to be applicable, the type argument needs to be within its bounds. It is not because, as we've shown above, ArrayList is not a subtype of Iterable<?>.

    So why does it compile anyways?

    It's a bug. Following a bug report and subsequent fix the JDT compiler explicitly rules out the first case (type argument containment). The second case is still happily ignored, because the JDT considers ArrayList to be a subtype of Iterable<?> (TypeBinding.isCompatibleWith(TypeBinding)).

    I don't know why javac behaves the same, but I assume for similar reasons. You will notice that javac does not issue an unchecked warning when assigning a raw ArrayList to an Iterable<?> either.

    0 讨论(0)
  • 2021-02-07 05:52

    If the type parameter were a wildcard-parameterized type, then the problem does not occur:

    Class<ArrayList<?>> foo = null;
    f(foo);
    g(foo);
    

    I think this is almost certainly a weird case arising out of the fact that the type of the class literal is Class<ArrayList>, and so the type parameter in this case (ArrayList) is a raw type, and the subtyping relationship between raw ArrayList and wildcard-parameterized ArrayList<?> is complicated.

    I haven't read the language specification closely, so I'm not exactly sure why the subtyping works in the explicit type parameter case but not in the wildcard case. It could also very well be a bug.

    0 讨论(0)
  • 2021-02-07 05:58

    Guess: The thing representing the first ? (ArrayList) does not 'implement' ArrayList<E> (by virtue of the double nested wildcard). I know this sounds funny but....

    Consider (for the original listing):

     void g(Class<? extends Iterable<Object> x) {} // Fail
     void g(Class<? extends Iterable<?> x) {}  // Fail
     void g(Class<? extends Iterable x) {}  // OK
    

    And

    // Compiles
    public class Test{
        <T extends Iterable<?>> void f(ArrayList<T> x) {}
        void g(ArrayList<? extends Iterable<?>> x) {}
    
        void d(){
            ArrayList<ArrayList<Integer>> d = new ArrayList<ArrayList<Integer>>();
            f(d);
            g(d);
        }
    }
    

    This

    // Does not compile on g(d)
    public class Test{
        <T extends Iterable<?>> void f(ArrayList<T> x) {}
        void g(ArrayList<? extends Iterable<?>> x) {}
    
        void d(){
            ArrayList<ArrayList> d = new ArrayList<ArrayList>();
            f(d);
            g(d);
        }
    }
    
    0 讨论(0)
  • 2021-02-07 05:58

    These are not quite the same:

    <T extends Iterable<?>> void f(Class<T> x) {}
    void g(Class<? extends Iterable<?>> x) {}
    

    The difference is that g accepts a "Class of unknown that implements Iterable of unknown", but ArrayList<T> is constrained implementing Iterable<T>, not Iterable<?>, so it doesn't match.

    To make it clearer, g will accept Foo implements Iterable<?>, but not AraryList<T> implements Iterable<T>.

    0 讨论(0)
提交回复
热议问题