Reputation: 2038
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)
Upvotes: 12
Views: 9828
Reputation: 8178
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.
Upvotes: 2
Reputation: 8078
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) {
...
}
Upvotes: 7