【问题标题】:Java type inference not working as I expectJava 类型推断没有像我预期的那样工作
【发布时间】:2013-10-09 21:44:41
【问题描述】:

我有这个方法(这是我原来问题的简化):

public List<AbstractMap.SimpleEntry<String, List<?>>> method(List<?> list) {
    return Collections.singletonList(new AbstractMap.SimpleEntry<>("", list));
}

但是,这会导致编译错误:

Console.java:40: error: incompatible types
        return Collections.singletonList(new AbstractMap.SimpleEntry<>("", list));
                                        ^
  required: List<SimpleEntry<String,List<?>>>
  found:    List<SimpleEntry<String,List<CAP#1>>>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
10 errors

如果我尝试在顶级方法上指定类型实例化:

return Collections.<AbstractMap.SimpleEntry<String, List<?>>>singletonList(new AbstractMap.SimpleEntry<>("", list));

我得到一个不同的错误:

Console.java:40: error: method singletonList in class Collections cannot be applied to given types;
return Collections.<AbstractMap.SimpleEntry<String, List<?>>>singletonList(new AbstractMap.SimpleEntry<>("", list));
                  ^
  required: T
  found: SimpleEntry<String,List<CAP#1>>
  reason: actual argument SimpleEntry<String,List<CAP#1>> cannot be converted to SimpleEntry<String,List<?>> by method invocation conversion
  where T is a type-variable:
    T extends Object declared in method <T>singletonList(T)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
10 errors

只有当我在 inner 方法上指定类型参数时,它才起作用:

return Collections.singletonList(new AbstractMap.SimpleEntry<String, List<?>>("", list));

我什至不假装理解这里发生了什么。我怀疑这与capture conversion(相关问题here)有关,但我不知道为什么在外部方法上指定泛型不起作用,但在内部方法上却可以。 java不使用返回类型来推断像这样的嵌套调用的类型参数吗?这是怎么回事?

【问题讨论】:

  • 我不认为它是重复的。我最初只使用方法调用而不是构造函数遇到了这个问题。由于 &lt;&gt; 的方法和构造函数使用相同的推理逻辑,因此我将其替换为复制原始问题的纯 JDK 版本。这个问题也主要是关于显式类型声明的位置差异,而不是推断任何东西。

标签: java generics type-inference


【解决方案1】:

问题是你不能用通配符类型参数实例化任何类(例如new ArrayList&lt;?&gt;(); 不会编译)。因此编译器尝试为 CAP#1 推导出一个现有的类,这是不可能的,因为需要通配符。

解决方案是避免使用通配符。像这样向 Method 本身添加一个 Type 参数。

public <T> List<AbstractMap.SimpleEntry<String, List<T>>> method(List<T> list) {
    return Collections.singletonList(new AbstractMap.SimpleEntry<>("", list));
}

除了编译器关闭签名之外,还可以更清楚地从方法中返回什么。

【讨论】:

  • 我不是直接实例化通配符,通配​​符是嵌套的(new ArrayList&lt;List&lt;?&gt;&gt;()确实编译),这是允许的。
  • 另外,在我的实际源代码中,通配符类型是从方法调用中返回的,所以我不能轻易摆脱它。
  • -1 嵌套通配符不捕获。您正在使用嵌套类型参数更改方法的行为。
  • @PaulBellora 你是对的。似乎 dimond 运算符完全无法从周围的方法调用中推断出类型。 f(new ArrayList&lt;&gt;())void f(ArrayList&lt;String&gt; l){} 不会编译。
猜你喜欢
  • 2021-02-08
  • 2016-06-19
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-01
  • 2014-06-28
  • 2020-08-24
相关资源
最近更新 更多