【问题标题】:Generics type inference fails?泛型类型推断失败?
【发布时间】:2010-11-10 22:41:30
【问题描述】:

示例 A

研究以下sn-p:

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 类型。

【问题讨论】:

  • 再次好,多基因润滑剂 :)
  • 好消息!这些示例现在可以在 Java 8 中编译。也许目标类型可以节省时间?

标签: java generics type-inference


【解决方案1】:

您对anyPair() 的第二次调用无法确定其类型,因此默认为&lt;Object, Object&gt;

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

当它处理anyPair() 时,没有可用的类型信息,因为它不知道它是process 的一部分。它默认为&lt;Object, Object&gt;,然后在查看process 时会导致类型不匹配。

第二个例子也发生了同样的事情。 Collections.emptySet()需要自己处理,但是无法确定需要的Types。

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

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

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

【讨论】:

  • 第二种方式:传入GenericsQ.&lt;String, Integer&gt;anyPair()
  • 你能详细说明一下吗?我以前从未见过GenericsQ,也找不到任何关于它的信息。
  • 这是定义该方法的类名。Polygenelubricants 将其编辑为ExampleA
  • 啊,谢谢,这就解释了为什么我没有看到它。我将其添加到我的答案中,因为我错了只有一种方法可以解决它。
  • 变量赋值起作用的原因是你已经指定了变量的类型。 Pair&lt;String,Integer&gt; p = anyPair(); 可以查看分配给它的内容并从中推断出类型。 process(p, anyPair()); 无法查看 process,直到它确定了每个参数的类型以确定要使用的签名。你也可以有一个static &lt;F,S&gt; void process(Pair&lt;F,S&gt; p1, Object p2) 方法,所以编译器需要首先评估参数。这意味着它不能从方法签名中获取任何信息,包括类型推断。
【解决方案2】:

为什么:

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

解决方案:

使用赋值推断类型:

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());
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-21
    • 1970-01-01
    • 2020-12-30
    相关资源
    最近更新 更多