收集器与结果类型中的通配符不匹配

Collector doesn't match wildcard in result type

我有一个数据对象列表,需要以各种方式对其进行分组,然后对分组结果执行常见操作。因此,我试图将常用操作提取到单个方法中,如以下人为设计的示例所示:

private static void print(List<Integer> data, 
                          Collector<Integer, ?, Map<?, List<Integer>>> collector) {
    data.stream().collect(collector)
            .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,String>groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,Integer>groupingBy(i -> i % 2));
}

请注意,在 print 方法中,收集器的结果类型是具有未知键 Map<?, List<Integer>>Map,因为当我按幅度打印时,我使用 String键,当我按模数打印时,我使用 Integer 键。

这段代码在对 Collectors.groupingBy 的两次调用中给出了两个编译错误。第一个抱怨使用 String 键的调用:

Error:(19, 58) java: incompatible types:
java.util.stream.Collector<java.lang.Integer,capture#1 of ?,
    java.util.Map<java.lang.String,java.util.List<java.lang.Integer>>>
cannot be converted to
java.util.stream.Collector<java.lang.Integer,?,
    java.util.Map<?,java.util.List<java.lang.Integer>>>

第二个抱怨使用 Integer 键的调用:

Error:(24, 59) java: incompatible types:     
java.util.stream.Collector<java.lang.Integer,capture#2 of ?,     
    java.util.Map<java.lang.Integer,java.util.List<java.lang.Integer>>>
cannot be converted to 
java.util.stream.Collector<java.lang.Integer,?,
    java.util.Map<?,java.util.List<java.lang.Integer>>>

Collectors.groupingBy 的 return 类型是 <T, K> Collector<T, ?, Map<K, List<T>>>,所以在 MagnitudeModulus 的情况下应该是

Collector<Integer, ?, Map<String,List<Integer>>>
Collector<Integer, ?, Map<Integer,List<Integer>>>

分别

为什么这些与 print

中的收集器参数不匹配
Collector<Integer, ?, Map<?, List<Integer>>> collector

?

简单写一下:

private static <T, U> void print(List<T> data, 
        Collector<T, ?, Map<U, List<T>>> collector) {
    data.stream().collect(collector)
    .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer, String>groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer, Integer>groupingBy(i -> i % 2));
}

这里的区别是我用通用参数替换了无限通配符类型('?'字符)(我使打印方法通用)。最大的区别是'?'无限通配符类型代表one type而不是any type 东西。因为你有两种类型的东西(整数和字符串),编译器在抱怨。

另请注意,您实际上并不需要调用带有参数的泛型方法(我认为从 Java 7 开始)。你也可以简单地写:

private static <T, U> void print(List<T> data, 
        Collector<T, ?, Map<U, List<T>>> collector) {
    data.stream().collect(collector)
    .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.groupingBy(i -> i % 2));
}

只需替换

private static <A, B> void print(List<Integer> data, Collector<Integer, ?, Map<A, List<B>>> groupingBy) {
        data.stream().collect(groupingBy).forEach((key, value) -> System.out.println(key + " : " + value));
    }

private static void print(List<Integer> data, 
                          Collector<Integer, ?, Map<?, List<Integer>>> collector) {
    data.stream().collect(collector)
            .forEach((key, list) -> System.out.println(key + ": " + list));
}

为什么?

因为 ? 仅适用于 Object Class ,甚至不适用于其子项 类

AnyType<?> object=new AnyType<Object> // true
AnyType<?> object=new AnyType<Number> // since Number is subchild of Object but Still false

编辑:

在您的方法 printByModulus() 中您传递了 Collectors.<Integer, Integer> 而在方法 printByMagnitude() 中您传递了 Collectors.<Integer, String> 是您出错的唯一原因。

用一个更简单的例子来解释,如果你有一个方法foo(Number),你可以传入一个Integer,因为IntegerNumber的子类型。但是,如果你有一个方法foo(List<Number>),你不能传入一个List<Integer>,因为List<Integer>不是List<Number>的子类型。

但是,如果您的方法 foo 只想从 List 中检索 Number,您可以将签名更改为 foo(List<? extends Number>)(另请参阅 “What is PECS”), 以允许使用 Number 的子类型对列表进行参数化。 List<Integer>List<? extends Number>

的子类型

更复杂一点,Map<Integer,List<Integer>>Map<String,List<Integer>> 都是 Map<?,List<Integer>> 的子类型,但是 Collector< … Map<Integer,List<Integer>> >Collector< … Map<String,List<Integer>> >不是 Collector< … Map<?,List<Integer>> > 的子类型。

解决方法是一样的。你想从 Collector 检索 映射,所以你必须求助于“? extends …”,即使用类型 Collector<Integer, ?, ? extends Map<?, List<Integer>>>:

private static void print(List<Integer> data, 
                      Collector<Integer, ?, ? extends Map<?, List<Integer>>> collector) {
    data.stream().collect(collector)
            .forEach((key, list) -> System.out.println(key + ": " + list));
}

private static void printByMagnitude() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,String>groupingBy(i -> i < 5 ? "small" : "large"));
}

private static void printByModulus() {
    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    print(data, Collectors.<Integer,Integer>groupingBy(i -> i % 2));
}