Invocation of java generic method
Given the generic method:
<T> List<T> getGenericList(int i) {...}
the following code compiles without any warning:
public List<String> getStringList(boolean b){
if(b)
return getGenericList(0);
else
开发者_JAVA技巧 return getGenericList(1);
}
but this one generates 'Type mismatch' compilation error:
public List<String> getStringList(boolean b) {
return (b) ? getGenericList(0) : getGenericList(1);
}
Why?
This is NOT a generics problem, but a consequence of the way the compiler has to infer the type of the ternary expression.
It happens the same with this equivalent code. This code works:
public byte function(boolean b){
if(b)
return 1;
else
return 2;
}
While this doesn't:
public byte function(boolean b) {
return (b) ? 1 : 2;
}
The reason is that when the compiler tries infer the type of this expression
return (b) ? 1 : 2;
It first has to obtain the type of each one of the operands, and check if they are compatible (reference) to evaluate wether the ternary expression is valid or not. If the type of the "return" were propagated to automatically cast or promote each one of the operands, it could lead to resolve the type of a ternary expression differently depending on the context of this expression.
Given that the type of the "return" cannot be propagated to the operands, then in the menctioned case:
return (b) ? getGenericList(0) : getGenericList(1);
the binding of the generic type cannot be done, so the type of each one of the operands is resolved to List<Object>
. Then the compiler concludes that the type of the whole expression is List<Object>
, which cannot be automatically casted to List<Integer>
(because they are not compatible types).
Whereas this other one
return getGenericList(0);
It applyes the type of the "return" to bind the generic type T, so the compiler concludes that the expression has a List<String>
type, that can be returned safely.
this is because of an edge case in generic type deduction
in the explicit returns the return type of each getGenericList
can be trivially set to List (outward info propagates inwards)
but in the conditional it goes the other way the type of it is the more general of the two possibilities (inward info propagates outwards)
the compiler could deduct the info implicitly here but it's not buildin yet file a bug report if you really need it
When the trinary operator is evaluated, it's result is not bound to any type. It's as if you just call:
getGenericList(0);
Try compiling the above, the compilation will fail.
In return statement, the result is bound to your function's return type and is evaluated.
Edit:
I've mistaken. The above statement compiles, but the result type is evaluated as (List < Object > ). Try compiling:
List<String> l = (List<String>)getGenericList(0);
This one will fail.
This is because javac needs to infer T
, but T
does not appear in argument types.
static<T> T foo(){ .. }
foo(); // T=?
Only in 2 cases, javac can infer T
from the context
String s = foo(); // #1: assignment
String bar(){
return foo(); // #2: return
In other cases, javac won't, and T
is simply inferred as Object
But this type of methods are dangerous anyway. How could your method know it's time to return List<String>
, not a list of something else? There's no info about T
available to you.
It looks likes its just inefficient compilation.
As Maurice said, generally, the compiler determines the type of T by function arguments. In this case there aren't any.
However, because getStringList()
returns List<String>
and it calls return getGenericList()
, the compiler is smart enough to forward the return type of getStringList to getGenericList and determine the type in that manner.
My guess is that in the ternary operator, it's doesn't bind in reverse, but rather finds the common denominator in each substatement and assigns that as the output of the ternary statement, and the compiler isn't smart enough to pass the expected output of the ternary statement into its substatements.
Note, you can directly pass the type parameter into the function call, and it works fine, ie:
return b ? this.<String> getGenericList(0) : this.<String> getGenericList(1);
compiles properly.
精彩评论