有人可以解释一下Java 8中使用的通用逻辑
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
根据PECS规则生产者扩展和消费者超级。但是我很难将这个逻辑放到第一个参数或第二个toMap中,这是keyMapper。
答案 0 :(得分:2)
有关PECS的一般信息
你必须这样考虑PECS:
制作人
extends
假设你有List<? extends Number>
。该列表将始终生成它正在扩展的类型,例如Number
。但是你并不确切知道哪种类型,你只知道给定列表的常见超类型。请看以下示例:
List<Integer> ints = Arrays.asList(1,2,3);
List<? extends Number> nums = ints;
Number n = ints.get(0); // will be 1
我们正在创建Integer
的列表,然后将其分配给我们的其他列表nums
。这是有效的,因为nums
返回的值始终为Number
或子类型。在这种情况下,Integer
。但是,您无法在列表中添加内容。因为您不知道实际列表真正由nums
表示。例如。尝试将long
添加到nums
将失败。因为真实列表仅支持Integer
s:
nums.add(1L); // compiler error
消费者
super
这恰恰相反。让我们再次说你有一个列表,例如List<?
super Number>
。该列表将始终接受Number
及其子类型添加到列表中:
List<Object> objs = new ArrayList<>();
List<? extends Number> nums = objs;
nums.add(1);
nums.add(2L);
nums.add(3F);
因为实际列表是objs
,它可以接受上述任何Object
个实例。虽然在尝试向Number
添加不同于nums
的内容时,编译器会抱怨。因为它无法直接验证列表是否真的可以接受该对象:
nums.add("4"); // compiler error
objs.add("4"); // works still
现在的问题是,你真的不知道你要添加什么内容list
。这使得无法定义使用get(0)
之类的方法时应返回的内容的一般行为。因此,java只需返回Object
:
Object o = nums.get(0);
相当于::
Object o = objs.get(0);
回答问题
因此,当应用于您的真实问题时。 keyMapper
可以接受任何类型匹配T
的对象,并生成任何类型匹配K
的对象。再一个例子可能会让你朝着正确的方向前进:
假设我们对类型String
使用T
,为Number
类型使用K
,它为我们提供了一个参数签名,如:Function<? extends String, ? super Number> keyMapper
。
现在我们可以使用我上面说过的东西来玩这个签名了。假设我们定义了从Object
到Long
的映射器:
Function<Object, Long> objToLong = (Object o) -> 1L;
然后可以将此映射器分配给keyMapper
:
Function<? extends String, ? super Number> keyMapper = objToLong;
因为objToLong
可以接受String
并生成Number
。关于valueMapper
。
我希望这个很长的答案可以帮助您更好地理解PECS
:)