给出通用方法:
<T> List<T> getGenericList(int i) {...}
以下代码编译时没有任何警告:
public List<String> getStringList(boolean b){
if(b)
return getGenericList(0);
else
return getGenericList(1);
}
但是这个会生成'Type mismatch'编译错误:
public List<String> getStringList(boolean b) {
return (b) ? getGenericList(0) : getGenericList(1);
}
为什么?
答案 0 :(得分:7)
这是不是泛型问题,而是编译器必须推断三元表达式类型的结果。
这个等效代码也是如此。此代码有效:
public byte function(boolean b){
if(b)
return 1;
else
return 2;
}
虽然不是这样:
public byte function(boolean b) {
return (b) ? 1 : 2;
}
原因是当编译器尝试推断该表达式的类型时
return (b) ? 1 : 2;
首先必须获取每个操作数的类型,并检查它们是否兼容(reference)以评估三元表达式是否有效。 如果“返回”的类型被传播以自动转换或提升每个操作数,则可能会导致根据上下文这个表达。
鉴于“return”的类型无法传播到操作数,那么在所提到的案例中:
return (b) ? getGenericList(0) : getGenericList(1);
无法完成泛型类型的绑定,因此每个操作数的类型都解析为List<Object>
。然后编译器得出结论:整个表达式的类型是List<Object>
,不能自动转换为List<Integer>
(因为它们不兼容类型)。
而另一个
return getGenericList(0);
它应用“return”的类型来绑定泛型类型T,因此编译器断定表达式具有List<String>
类型,可以安全返回。
答案 1 :(得分:5)
当评估三元运算符时,它的结果不受任何类型的约束。这就像你打电话:
getGenericList(0);
尝试编译以上内容,编译将失败。
在return语句中,结果绑定到函数的返回类型并进行求值。
修改强>
我错了。上面的语句编译,但结果类型被评估为(List&lt; Object&gt;)。尝试编译:
List<String> l = (List<String>)getGenericList(0);
这个会失败。
答案 2 :(得分:5)
这是因为泛型类型推导的边缘情况
在显式返回中,每个getGenericList
的返回类型可以简单地设置为List(向外信息向内传播)
但是在条件中它走另一种方式它的类型是两种可能性中更一般的(向内信息向外传播)
编译器可以在这里隐式地扣除信息,但是如果你确实需要的话,它还没有构建错误报告
答案 3 :(得分:2)
这是因为javac需要推断T
,但T
不会出现在参数类型中。
static<T> T foo(){ .. }
foo(); // T=?
仅在2个案例中,javac可以从上下文中推断T
String s = foo(); // #1: assignment
String bar(){
return foo(); // #2: return
在其他情况下,javac不会,T
只是推断为Object
但是这种方法无论如何都是危险的。您的方法怎么知道是时候返回List<String>
,而不是其他内容的列表?没有关于T
可用的信息。
答案 4 :(得分:1)
看起来它只是效率低下的编译。
正如Maurice所说,通常,编译器通过函数参数确定T的类型。在这种情况下,没有任何。
但是,因为getStringList()
返回List<String>
并且它调用return getGenericList()
,所以编译器足够聪明,可以将getStringList的返回类型转发到getGenericList,并以这种方式确定类型。
我的猜测是,在三元运算符中,它不会反向绑定,而是在每个子语句中找到公分母并将其指定为三元语句的输出,并且编译器不够智能不能通过三元语句的预期输出到其子语句中。
注意,你可以直接将type参数传递给函数调用,它工作正常,即:
return b ? this.<String> getGenericList(0) : this.<String> getGenericList(1);
正确编译。