Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't javac infer generic type arguments for functions used as arguments?

In the following sample, why is the compiler able to infer the generic arguments for the first call to Foo.create() in Foo.test(), but not able to do so in the second? I'm using Java 6.

public class Nonsense {
    public static class Bar {
        private static void func(Foo<String> arg) { }
    }

    public static class Foo<T> {

        public static <T> Foo<T> create() {
            return new Foo<T>();
        }

        private static void test() {
            Foo<String> foo2 = Foo.create(); // compiles
            Bar.func(Foo.create());          // won't compile
            Bar.func(Foo.<String>create());  // fixes the prev line
        }
    }
}

(The compile error is The method func(Nonsense.Foo) in the type Nonsense.Bar is not applicable for the arguments (Nonsense.Foo)).

Note: I understand the compiler error can be fixed by the third line in test() - I'm curious as to whether there is a specific limitation that prevents the compiler from being able to infer the type. It appears to me that there is enough context for it here.

like image 267
bacar Avatar asked Feb 01 '13 17:02

bacar


People also ask

What is infer generic type arguments?

Type inference represents the Java compiler's ability to look at a method invocation and its corresponding declaration to check and determine the type argument(s). The inference algorithm checks the types of the arguments and, if available, assigned type is returned.

Which types can be used as arguments of a generic type?

The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).

How do you declare a generic type variable in Java?

It specifies the type parameters (also called type variables) T1, T2, ..., and Tn. To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class.

How does a generic method differ from a generic type?

From the point of view of reflection, the difference between a generic type and an ordinary type is that a generic type has associated with it a set of type parameters (if it is a generic type definition) or type arguments (if it is a constructed type). A generic method differs from an ordinary method in the same way.


1 Answers

As of Java 7, method overload resolution has to proceed before any target type information from the method you are calling can be taken into account to try to infer the type variable T in the declaration of func. It seems foolish, since we can all see that in this case there is one and only one method named func, however, it is mandated by the JLS and is the behavior of javac from Java 7.

Compilation proceeds as follows: First, the compiler sees that it is compiling a call to a static method of class Bar named func. To perform overload resolution, it must find out what parameters the method is being called with. Despite this being a trivial case, it must still do so, and until it has done so it does not have any information about the formal parameters of the method available to help it. The actual parameters consist of one argument, a call to Foo.create() which is declared as returning Foo<T>. Again, with no criteria from the target method, it can only deduce that the return type is the erasure of Foo<T> which is Foo<Object>, and it does so.

Method overload resolution then fails, since none of the overloads of func is compatible with an actual parameter of Foo<Object>, and an error is emitted to that effect.

This is of course highly unfortunate since we can all see that if the information could simply flow in the other direction, from the target of the method call back towards the call site, the type could readily be inferred and there would be no error. And in fact the compiler in Java 8 can do just that, and does. As another answer stated, this richer type inferencing is very useful to the lambdas that are being added in Java 8, and to the extensions to the Java APIs that are being made to take advantage of lambdas.

You can download a pre-release build of Java 8 with JSR 335 lambdas from the preceding link. It compiles the code in the question without any warnings or errors.

like image 197
David Conrad Avatar answered Oct 15 '22 20:10

David Conrad