泛型类型推断失败?

时间:2010-11-10 22:41:31

标签: java generics type-inference

示例A

研究以下片段:

public class ExampleA {
   static class Pair<F,S> { }

   static <F,S> Pair<F,S> anyPair() { return null; }

   static <F,S> void process(Pair<F,S> p1, Pair<F,S> p2) { return; }

   public static void main(String[] args) {
      Pair<String,Integer> p = anyPair();

      process(p, anyPair()); // doesn't compile
   }
}

有人可以解释为什么类型推断适用于局部变量p的赋值,而不适用于process的第二个实际参数吗?


例B

这可能更容易理解:

public class ExampleB {     
   public static <E> void process(Set<E> s1, Set<E> s2) { return; }

   public static void main(String[] args) {
      process(new HashSet<String>(), Collections.emptySet()); // doesn't compile
   }
}

同样的问题:为什么不编译?

我希望Collections.emptySet()只适用于 ANY 参数化Set类型。

2 个答案:

答案 0 :(得分:8)

您对anyPair()的第二次电话无法确定其类型,因此默认为<Object, Object>

编译器将process(p, anyPair());分成几部分并逐个处理。当它这样做时,它需要首先处理参数以确定它们的类型,然后在处理process时可以使用它们。

当它进入流程anyPair()时,没有可用于该作品的类型信息,因为它不知道它在那一点上是process的一部分。默认为<Object, Object>,在查看process时会导致类型不匹配。

第二个例子也是如此。 Collections.emptySet()需要自行处理,但无法确定所需的类型。

有两种方法可以解决这个问题:

第一种方法是为编译器提供类型推断所需的信息,方法与第一次调用anyPair()时相同,方法是将其存储在具有正确类型的临时变量中。

第二个(感谢@BalusC)是使用ExampleA.<String, Integer>anyPair()。此语法显式设置所需的类型,而不必超出调用范围。

答案 1 :(得分:0)

为什么:

Collections.emptySet()正在尝试推断要返回的类型。它无法使用,因为E可以是ObjectString。两者都是进程的有效匹配。默认情况下,通用参数始终不变,而不是逆变。这意味着Integer extends NumberList<Integer> 延伸List<Number>。但确实扩展List<? extends Number>

解决方案:

使用分配来推断类型:

public <E> void process(Set<E> s1, Set<E> s2) { return; }

public void main(String[] args) {
   Set<String> s = Collections.emptySet();  // add this line
   process(new HashSet<String>(), s);
}

明确写出类型:

public <E> void process(Set<E> s1, Set<E> s2) { return; }

public void main(String[] args) {
   process(new HashSet<String>(), Collections.<String>emptySet()); //notice <String>
}

明确允许逆转(但这可能不是你想要的):

public <E> void process(Set<E> s1, Set<? super E> s2) { // added "super"
  return;
}

public void main(String[] args) {
   process(new HashSet<String>(), Collections.emptySet());
}