Skip to content

Why compiler cannot infer generic types of the inner class in Java?

class A<T>
{
    class InnerA<U>
    {}
}

public class Main
{
    public static void main(String[] args)
    {
        A<Integer>.InnerA<String> var = new A<>().new InnerA<>();
    }
}

When the above code snippet is compiled, it leads to Compile-Time error saying “incompatible types: cannot infer type arguments for A.InnerA<>. It seems to me that the Compiler should be able to infer the types to be InnerA<String>.

Why is it not able to do so? It would be helpful if someone could explain how it works behind the scene.

Answer

The compiler error message is misleading.

The compiler can only infer the type of an expression from the left hand side of an assignment, if the expression is the one whose result is actually assigned. In your example, new A<>() is not assigned, but just used for a qualified instantiation of the inner class¹. Since there is no assignment context for new A<>(), the compiler infers new A<Object>().

For the .new InnerA<>(), there is an assignment context and the compiler tries to infer the result type using the left hand side of the assignment but fails because A’s type is already incompatible. In the shortened compiler message, the failure to infer a type for InnerA is visible but not the reason of the mismatching type argument for A.

The long error message looks like

Main.java:11: error: incompatible types: cannot infer type arguments for A.InnerA<>
        A<Integer>.InnerA<String> var = new A<>().new InnerA<>();
                                                            ^
  reason: no instance(s) of type variable(s) U exist
          so that A<Object>.InnerA<U> conforms to A<Integer>.InnerA<String>

  where U is a type-variable:
    U extends Object declared in class A.InnerA

So the issue still is reported as “can’t find a type for U” but we can also see that the deeper reason is that no U can ever solve the problem of having mismatching types for A<T>.

We can also demonstrate the actual cause by changing the line to

A<Integer>.InnerA<String> var = new A<>().new InnerA<String>();

providing the right type for InnerA and now getting

incompatible types:
    A<Object>.InnerA<String> cannot be converted to A<Integer>.InnerA<String>

or changing the line to

A<Integer>.InnerA<String> var = new A<Integer>().new InnerA<>();

which will solve the problem, as now A has the right type and the type for InnerA can be inferred from the left hand side.


¹ the same issue arises if you have genericMethod1().method2()