Generic method type inference when the target type has a wildcard

后端 未结 2 1712
野趣味
野趣味 2021-01-16 05:49

I understand that the compiler uses the target type to determine the type argument that makes the generic method invocation applicable. For instance, in the following statem

相关标签:
2条回答
  • 2021-01-16 06:25

    Each usage of a wildcard has a distinct type associated with it. (Usually the JLS refers to this as being a "fresh type".) This is how for example a compiler error like this works:

    List<?> list0 = ... ;
    List<?> list1 = ... ;
    list0.add(list1.get(0)); // error
    

    Because it's the case that list0 and list1 are given separate types by the compiler such that for the most part

    reference_type_of(List<?>) != reference_type_of(List<?>)
    

    You can begin to see how this fits in to type inference if you try something like

    {
        List<?> list0 = ... ;
        List<?> list1 = ... ;
        test(list0, list1);
    }
    static <T> void test(List<T> list0, List<T> list1) {}
    

    Where the compiler emits an error that actually tells us a little bit about the types that it has generated for list0 and list1.

    error: method test in class Ideone cannot be applied to given types;
        test(list0, list1);
        ^
      required: List<T>,List<T>
      found: List<CAP#1>,List<CAP#2>
      reason: no instance(s) of type variable(s) T exist so that
              argument type List<CAP#2> conforms to formal parameter type List<T>
      where T is a type-variable:
        T extends Object declared in method <T>test(List<T>,List<T>)
      where CAP#1,CAP#2 are fresh type-variables:
        CAP#1 extends Object from capture of ?
        CAP#2 extends Object from capture of ?
    

    (My emphasis in bold.) These CAP#... types were generated during capture conversion. What it's showing us is that when the method invocation expression was examined, list0 and list1 were given distinct types from each other. (And for those that need an explanation for the error: it's because the declaration of test asserts that both Lists must have the same T.)

    So since we now know that a wildcard gets associated a reference type, we can see that in a case like

    List<?> empty = Collections.emptyList();
    

    The invocation will be inferred as something like "a fresh type where the upper bound is Object". Or symbolically we could say the compiler might see something like

    // target type       -->       inferred invocation type
    //     v                           v
    List<CAP#1> empty = Collections.<CAP#1>emptyList();
    

    Although: of course we are always guessing a little bit because it's up to the compiler how it implements this. In theory, for a case like the above trivial assignment of emptyList(), it would not have to do work to generate the correct bytecode.

    Also, I am sorry, I don't feel like spec scouring today. Essentially the type inference here works by generating a set of constraints to demonstrate that the method invocation should or should not compile. The algorithm described in 18.5.2 incorporates a wildcard in this way.

    0 讨论(0)
  • 2021-01-16 06:30

    What is the inferred type in this case? Is it Object? Or it doesn't really matter due to the wildcard telling the compiler any type is possible?

    On one level, it's kind of a philosophical question, because the type argument does not have any effect on the compiled bytecode, so it doesn't really matter what it is specifically. The only thing that matters is whether or not it's impossible to satisfy the bounds and context. As long as the compiler can prove that there exists some type that could work, then in my opinion, it should be able to go ahead and compile it without needing to come up with an actual type.

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