dre*_*ash 7 java generics lambda
直奔主题(我知道应该避免使用通配符类型作为返回类型)
我正在写这个答案和以下代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
得到以下警告:
Required type: capture of ?
Provided: capture of ?
Run Code Online (Sandbox Code Playgroud)
以及 IntelliJ 的建议
将变量 'map' 更改为 'Map<?, Object'
这更没有意义。当然,当我尝试应用该建议时它失败了。
最初,我虽然“好吧,这与它与计算签名不匹配的事实有关”,即:
default V compute(K key, ...)
Run Code Online (Sandbox Code Playgroud)
所以我试过了
public class MyMap <T>{
public static <T> void nothing(Collection<T> c){
// Empty
}
}
Run Code Online (Sandbox Code Playgroud)
和
public static Map<?, Long> manualMap(Collection<Collection<?>> c, Map<?, Long> map){
c.forEach(MyMap::nothing);
return map;
}
Run Code Online (Sandbox Code Playgroud)
我没有问题。
以下两个版本:
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;
}
Run Code Online (Sandbox Code Playgroud)
和
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;
}
Run Code Online (Sandbox Code Playgroud)
工作没有任何问题(除了(T)情况下的警告)。
所以问题是
为什么第一个版本不起作用?
第一种方法由于“捕获转换”而无法编译,这发生在每个声明中。您可以阅读我关于此或此的其他答案。但简而言之,您将在那里有两种不同的类型,您可以通过以下方式进行编译:
javac --debug=verboseResolution=all
Run Code Online (Sandbox Code Playgroud)
输出将包含:
.....
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ?
...
Run Code Online (Sandbox Code Playgroud)
这意味着有两种类型已被捕获转换。这些类型彼此无关,就像您拥有它的方式一样。
另一方面:
public static <T> void nothing(Collection<T> c){
}
Run Code Online (Sandbox Code Playgroud)
被称为通配符捕获方法(它“捕获”通配符),并在官方教程中记录了它的工作原理和方式;因此你没有问题。
但这里的主要问题是您不能null为通配符分配任何内容(除了)。因此,在您的compute示例中,第一个参数将被推断为 a?并且您无法为其分配任何内容。
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;
}
Run Code Online (Sandbox Code Playgroud)
在这里,您有三个通配符:一个用于参数,一个用于映射变量,一个用于返回值。(返回值一不是超级相关)。
您正在尝试将元素从集合(一种通配符类型)传递到映射方法(另一种通配符类型)中。
编译器不知道这两个“意味着”相同,因此它不接受需要“地图”通配符的“集合”通配符。
您可以通过类型变量指示它们是相同的类型:
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;
}
Run Code Online (Sandbox Code Playgroud)
这里,T 是一个你不知道的类型;但你知道在这两种情况下它都是相同的未知类型。
或者您可以以不需要两个通配符的方式声明它:
return c.stream().collect(groupingBy(a -> a, counting()));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
115 次 |
| 最近记录: |