I have the following class with the following method.
public class MyClass {
public static <T> T getSomeThing(final int id, final java.lang.reflect.Type type, final Map<Integer, String> someThings) {
final String aThing = someThings.get(id);
if (aThing == null || aThing.isEmpty()) {
return null;
}
return GsonHelper.GSON.fromJson(aThing, type);
}
}
The GsonHelper provides me with some com.google.gson.GsonBuilder
public class GsonHelper {
public static final com.google.gson.Gson GSON = getGsonBuilder().create();
public static GsonBuilder getGsonBuilder() {
return new GsonBuilder().setPrettyPrinting()
.enableComplexMapKeySerialization()
.registerTypeAdapter(new com.google.gson.reflect.TypeToken.TypeToken<byte[]>() {
// no body
}.getType(), new Base64TypeAdapter())
.registerTypeHierarchyAdapter(Date.class, new DateTypeAdapter())
.registerTypeHierarchyAdapter(Pattern.class, new PatternTypeAdapter())
.registerTypeAdapterFactory(new ListTypeAdapterFactory())
.registerTypeAdapterFactory(new MapTypeAdapterFactory())
.registerTypeAdapterFactory(new SetTypeAdapterFactory());
}
}
Until Java 7 I was using this method like:
Map<Integer, String> allThings = new HashMap<>();
//FILL allThings with values
if(MyClass.getSomeThing(7, java.lang.Boolean.class, allThings)){
//do something
}
This worked fine. cause the method will return a Boolean and I can use this inside "if". But when I change to Java 8 this is not possible anymore. The compiler complains about:
Type mismatch: cannot convert from Object to boolean
//while this is working but would throw a JsonSyntaxException
final String myString = "myInvalidJsonString";
if(GsonHelper.GSON.fromJson(myString, java.lang.Boolean.class)){
//do something
}
I know that java.lang.Boolean can be null. And I could solve this issue with:
final Boolean b = MyClass.getSomeThing(7, java.lang.Boolean.class, allThings);
if(b){
//do something
}
But I am curious about why this is working with Java 7 and NOT in Java 8. (not answered)
What did they change? (not answered)
What is the reason for this compiler error when changing to Java 8? (answered)
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) {
...
}
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.