Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compilers behave differently with a null parameter of a generic method

The following code compiles perfectly with Eclipse, but fails to compile with javac:

public class HowBizarre {
      public static <P extends Number, T extends P> void doIt(P value) {
      }

      public static void main(String[] args) {
            doIt(null);
      }
}

I simplified the code, so T is not used at all now. Still, I don't see a reason for the error. For some reason javac decides that T stands for Object, and then complains that Object does not conform to the bounds of T (which is true):

HowBizarre.java:6: incompatible types; inferred type argument(s) java.lang.Number,java.lang.Object do not conform to bounds of type variable (s) P,T

found : <P,T>void

required: void

       doIt(null);
           ^

Note that if I replace the null parameter with a non-null value, it compiles fine.

Which of the compilers behaves correctly and why? Is this a bug of one of them?

like image 707
Eyal Schneider Avatar asked Jun 08 '10 18:06

Eyal Schneider


People also ask

What does the compiler do for you when you use generic types?

A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find. Elimination of casts. Enabling programmers to implement generic algorithms.

How does a generic method differ from a generic type in Java?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

Are generics type differ based on their type arguments?

Generic Types Differ Based on Their Type Arguments: Consider the following Java code.

How many types of parameters are there in generic programming?

As with generic methods, the type parameter section of a generic class can have one or more type parameters separated by commas.


2 Answers

The problem is due to a JLS specification that mandates that otherwise uninferrable type arguments must be inferred as Object, even if it doesn't satisfy the bounds (and would consequently trigger a compilation error).

The following is an excerpt from the "bug" report (which has been further annotated for clarity):

"Bug" ID 6299211 - method type variable: inference broken for null

This program does not compile:

public class Try {
    void m() {
        java.util.Collections.max(null);
    }
}

State: CLOSED, NOT A DEFECT.

Evaluation: THIS IS NOT A BUG. The inference algorithm cannot gather any information from the argument (null) and the method is not called in a place where there are any expectations on the returned value. In such cases the compiler must infer java.lang.Object for the type variable.


JLS 15.12.2.8 Inferring Unresolved Type Arguments

Any remaining type variables that have not yet been inferred are then inferred to have type Object


However, Object is not a subtype of Comparable<? super Object> and thus not within the bounds of the type variable in the declaration of Collections.max:

<T extendsObject & Comparable<? super T>> T max(Collection<? extends T>)


Further explorations

Using explicit type parameters "fixes" the problem:

HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac

To show that this has less to do with a null argument and more to do with the absolute lack of information for type inferrence, you can try e.g. either of the following declarations:

<T,U extends Comparable<T>> void doIt()

<T extends Number,U extends T> void doIt()

In either case, an invocation doIt(); doesn't compile in javac, as it must infer U to be Object as per 15.12.2.8, even if doing so would trigger a compilation error.


Note on Eclipse

While none of the snippets above compile in some version of javac, they all do in some version of Eclipse. This would suggest a bug on Eclipse's part. It's been known that there are disagreements between the different compilers.

Related questions

  • Generics compiles and runs in Eclipse, but doesn’t compile in javac
like image 159
7 revs Avatar answered Sep 17 '22 22:09

7 revs


It's rather a bug in javac. Eclipse infers the correct type.

You can work it around by calling doIt((Number) null);

Even if you don't plan to use javac for development, fix this issue, because tools like ant or maven use it and it will cause problems in case you introduce them at some point.

like image 39
Bozho Avatar answered Sep 16 '22 22:09

Bozho