Java 8泛型输入类型函数

时间:2018-01-10 17:10:38

标签: generics java-8

有人可以解释一下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。

1 个答案:

答案 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

现在我们可以使用我上面说过的东西来玩这个签名了。假设我们定义了从ObjectLong的映射器:

Function<Object, Long> objToLong = (Object o) -> 1L;

然后可以将此映射器分配给keyMapper

的签名
Function<? extends String, ? super Number> keyMapper = objToLong;

因为objToLong可以接受String并生成Number。关于valueMapper

也可以这样说

我希望这个很长的答案可以帮助您更好地理解PECS:)