java - 为什么我不能在参数中使用通配符类型来计算
问题描述
直奔主题(我知道应该避免使用通配符类型作为返回类型)
我正在写这个答案和以下代码:
public static Map<?, Long> manualMap(Collection<?> c){
Map<?, Long> map = new HashMap<>();
c.forEach(e -> map.compute(e, (k, v) -> (v == null) ? 1 : v + 1));
return map;
}
收到以下警告:
Required type: capture of ?
Provided: capture of ?
以及来自 IntelliJ 的建议
将变量 'map' 更改为 'Map<?, Object'
这更没有意义。自然,当我尝试应用该建议时它会失败。
最初,我虽然“好吧,这与它与计算签名不匹配的事实有关”,即:
default V compute(K key, ...)
所以我尝试了
public class MyMap <T>{
public static <T> void nothing(Collection<T> c){
// Empty
}
}
和
public static Map<?, Long> manualMap(Collection<Collection<?>> c, Map<?, Long> map){
c.forEach(MyMap::nothing);
return map;
}
我没有问题。
以下两个版本:
public static <T> Map<?, Long> manualMap(Collection<?> c){
Map<T, Long> map = new HashMap<>();
c.forEach(e -> map.compute((T) e, (k, v) -> (v == null) ? 1 : v + 1));
return map;
}
和
public static Map<?, Long> manualMap(Collection<?> c){
Map<Object, Long> map = new HashMap<>();
c.forEach(e -> map.compute(e, (k, v) -> (v == null) ? 1 : v + 1));
return map;
}
工作没有任何问题(除了(T)情况下的警告)。
所以问题是
为什么第一个版本不起作用?
解决方案
第一种方法由于“捕获转换”而无法编译,这发生在每个声明中。你可以阅读我关于这个或这个的其他答案。但简单地说,您将在那里有两种不同的类型,您可以在编译时看到它们:
javac --debug=verboseResolution=all
输出将包含:
.....
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
...
这意味着有两种类型已被捕获转换。这些类型彼此无关,就像你拥有它的方式一样。
另一方面:
public static <T> void nothing(Collection<T> c){
}
被称为通配符捕获方法(它“捕获”通配符),并在官方教程中记录了它的工作原理和方式;因此,您对此没有任何问题。
但这里的主要问题是您不能将任何东西(除了null
)分配给通配符。因此,在您的compute
示例中,第一个参数将被推断为 a?
并且您不能为其分配任何内容。
推荐阅读
- mysql - 在 INSERT > SELECT 中使用带有 CONCAT_WS 的变量作为表名
- python - 如何将数据框中的特定列与同一数据框中的一个特定列相乘?
- swift - 无法调用非函数类型“[成分]”的值
- python - 在networkx中添加多个有向边
- javascript - 使用本地存储制作排行榜但不保存
- python - 尝试将文件上传到 Sharepoint 时收到“ShareplumRequestError: Sharepoint HTTP Post/Get Failed”
- javascript - 从 FiveM 获取信息的命令
- linux - 无法以用户身份启动 VS Code 远程桌面,但可以通过终端以用户身份登录
- r - 将数据框添加到列表并在 R 中重新运行循环
- python - 如何用曲线拟合拟合excel文件的数据?