繁体   English   中英

为什么我不能在参数中使用通配符类型来计算

[英]Why I cannot use wildcard type in parameter to compute

直奔主题(我知道应该避免使用通配符类型作为返回类型

我正在写这个答案和以下代码:

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

output 将包含:

.....
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
...

这意味着有两种类型已被捕获转换。 这些类型彼此无关,就像你拥有它的方式一样。

另一方面:

public static <T> void nothing(Collection<T> c){

}

被称为通配符捕获方法(它“捕获”通配符),并在官方教程中记录了它的工作原理和方式; 因此,您对此没有任何问题。


但这里的主要问题是您不能将任何东西(除了null )分配给通配符。 因此,在您的compute示例中,第一个参数将被推断为? 你不能为此分配任何东西。

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

在这里,您有三个通配符:一个在参数上,一个在 map 变量上,一个在返回值上。 (返回值 one 不是超级相关的)。

您正在尝试将集合中的元素(一种通配符类型)传递到 map (另一种通配符类型)的方法中。

编译器不知道这两个“应该是”相同的,因此它不接受需要“map”通配符的“collection”通配符。

您可以通过类型变量表明它们是相同的类型:

public static <T> Map<?, Long> manualMap(Collection<T> c){
    Map<T, Long> map = new HashMap<>();
    c.forEach(e -> map.compute(e, (k, v) -> (v == null) ? 1 : v + 1));
    return map;
}

这里,T 是你不知道的类型; 但是您知道在这两种情况下它都是相同的未知类型。

或者您可以以不需要两个通配符的方式声明它:

return c.stream().collect(groupingBy(a -> a, counting()));

问题是您的方法正在接受“我不在乎”类型的集合,但您也在创建一个带有“我不在乎”键的 map。 这并不意味着您不关心的类型是相同的类型。 为此,您需要通过使用类型变量来实际绑定类型,例如

public static <T> Map<T, Long> manualMap(Collection<T> c){
    Map<T, Long> map = new HashMap<>();
    c.forEach(e -> map.compute(e, (k, v) -> (v == null) ? 1 : v + 1));
    return map;
}

该方法仍将接受任何类型的 collections,但它足够关心跟踪类型并且任何地方都没有通配符或警告,是的!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM