Type mismatch error in java 8

The last version where the result of getSomeThing() is assigned to a Boolean variable can be inferred according to JLS 8, because in an assignment context the target type Boolean lets T to be inferred as Boolean, indeed. Here all compilers agree.

Regarding the original case, JLS 8 does not classify the condition of an if-statement as an assignment context. Outside assignment or invocation contexts, the invocation is not treated as a poly expression, but as a standalone expression (JLS 15.12 1st bullet). Standalone expressions have no target type. Without a target type inference in Java 8 falls back to inferring T to Object.

The Eclipse team has requested clarification in this regard even before Java 8 GA. Unfortunately, the resulting issue remains unresolved until today.

Ergo: JLS and the observed behavior of javac don't seem to agree. Likely, JLS is the entity that should be fixed.

Update: JLS is not going to change (confirmed via private email), hence accepting the program is a bug in javac.

Edit: Javac version 12 will propagate this bug even through a switch expression:

public class X {
    @SuppressWarnings("preview")
    public void foo(int i) {
        if (switch(i) { default -> magic(); })
            System.out.println("true");
    }
    <T> T magic() { return null; }
}

javac accepts this due to type inference with target type Boolean, but performing type inference in this location is illegal per JLS.


Your method public static <T> T getSomeThing(final int id, final java.lang.reflect.Type t, final Map<Integer, String> someThings) does not guarantee to return a Boolean. It returns T which is defined by the caller and could be anything, which means Object.

The if statement can't know which type T will have and hence can't guarantee to convert it to a boolean.

Why not change the signature to boolean?

public static boolean getSomeThing(final int id,
                                   final java.lang.reflect.Type t,
                                   final Map<Integer, String> someThings)

Or are you in search of this?

public static <T> T getSomeThing(final int id,
                                 final Class<T> clazz,
                                 final Map<Integer, String> someThings)

Than this code will compile and work:

public static void main(String[] args) {
    if (getSomeThing(7, Boolean.class, emptyMap())) {
        System.out.println("It works!");
    }
}

public static <T> T getSomeThing(final int id,
                                 final Class<T> clazz,
                                 final Map<Integer, String> someThings) {
    ...
}