generics类型推断失败了吗?
例A
研究以下片段:
public class ExampleA { static class Pair { } static Pair anyPair() { return null; } static void process(Pair p1, Pair p2) { return; } public static void main(String[] args) { Pair p = anyPair(); process(p, anyPair()); // doesn't compile } }
有人可以解释为什么类型推断适用于局部变量p
的赋值但不适用于要process
的第二个实际参数吗?
例B
这可能更容易理解:
public class ExampleB { public static void process(Set s1, Set s2) { return; } public static void main(String[] args) { process(new HashSet(), Collections.emptySet()); // doesn't compile } }
同样的问题:为什么不编译?
我希望Collections.emptySet()
只适用于任何参数化的Set
类型。
您对anyPair()
第二次调用没有任何方法来确定它的类型,因此它默认为 。
编译器正在破坏process(p, anyPair());
把它分成几块并单独处理。 当它这样做时,它需要首先处理参数以确定它们的类型,然后可以在处理process
时使用它们。
当它去处理anyPair()
,没有可用于该片段的类型信息,因为它不知道它是该点的process
一部分。 它默认为 ,这会在查看
process
时导致类型不匹配。
你的第二个例子也发生了同样的事情。 Collections.emptySet()
需要自己处理,但无法确定所需的类型。
有两种方法可以解决这个问题:
第一种方法是为编译器提供类型推断所需的信息,方法与第一次调用anyPair()
,方法是将其存储在具有正确类型的临时变量中。
第二个(感谢@BalusC)是使用ExampleA.
。 此语法显式设置所需的类型,而不必超出调用范围。
为什么:
Collections.emptySet()
试图推断要返回的类型。 它不能,因为E可能是Object
或String
。 两者都是进程的有效匹配。 默认情况下,通用参数始终不变,而不是逆变。 这意味着Integer extends Number
但List
没有扩展List
。 它确实扩展了List extends Number>
但是, List extends Number>
。
解决方案:
使用赋值来推断类型:
public void process(Set s1, Set s2) { return; } public void main(String[] args) { Set s = Collections.emptySet(); // add this line process(new HashSet (), s); }
明确写出类型:
public void process(Set s1, Set s2) { return; } public void main(String[] args) { process(new HashSet(), Collections. emptySet()); //notice }
明确允许逆转(但这可能不是你想要的):
public void process(Set s1, Set super E> s2) { // added "super" return; } public void main(String[] args) { process(new HashSet(), Collections.emptySet()); }