使用方法返回的Collector时泛型类型不匹配- Java 17



我正在试验records

我创建了这些记录来计算文本中的字母数量。

record Letter(int code) {
Letter(int code) {
this.code = Character.toLowerCase(code);
}
}
record LetterCount(long count) implements Comparable<LetterCount> {
@Override
public int compareTo(LetterCount other) {
return Long.compare(this.count, other.count);
}
static Collector<Letter, Object, LetterCount> countingLetters() {
return Collectors.collectingAndThen(
Collectors.<Letter>counting(), 
LetterCount::new);
}
}

下面是使用它们的代码片段:

final var countedChars = text.chars()
.mapToObj(Letter::new)
.collect(
groupingBy(Function.identity(), 
LetterCount.countingLetters()                      // compilation error
// collectingAndThen(counting(), LetterCount::new) // this line doesn't produce error
));

如果我注释掉collectingAndThen()作为groupingBy()中的下游收集器,上面显示的代码片段不会产生错误。

然而,当我试图使用LetterCount.countingLetters()作为下游收集器时,编译器会丢失。

我得到以下错误信息:

Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
Collector cannot be resolved to a type
Type mismatch: cannot convert from Collector<Letter,capture#17-of ?,LetterCount> to Collector<Letter,Object,LetterCount>
The method countingLetters() from the type LetterCount refers to the missing type Collector
The method entrySet() is undefined for the type Object

接口Collector有如下声明:

public interface Collector<T,​A,​R>

其中第二个泛型类型参数A表示可变容器的类型,该容器在内部用于累积约简结果。此类型通常被实现隐藏。

方法countingLetters()声明返回类型如下:

Collector<Letter, Object, LetterCount>

这意味着该方法返回的收集器的可变容器的类型应该是Object

提示:泛型是不变量,即如果你说Object,你必须只提供Object(不是它的子类型,不是未知类型?,只有Object类型本身)。

这是不正确的,原因如下:

  • JDK中内置的所有收集器都隐藏了它们的可变容器类型。您在代码中使用的收集器conting声明返回Collector<T,?,Long>。在底层,它使用summingLong,它反过来返回Collector<T,?,Long>,尽管它内部使用long[]作为容器。这是因为公开这些实现细节没有意义。因此,您声明的返回类型的泛型参数与您返回的收集器的泛型参数不一致。也就是说,既然给定一个未知类型?,你就不能声明返回一个Object

  • 即使您不使用内置收集器,而是使用您自己的自定义收集器,暴露其容器实际类型仍然不是一个非常聪明的主意。只是因为在将来的某个时候,您可能想要更改它。

  • Object类是不可变的,因此使用它作为容器类型(如果您尝试实现自定义收集器)是徒劳的,因为它不能积累数据。


底线:countingLetters()方法返回的收集器中的第二个泛型参数不正确。

要解决这个问题,必须将可变容器的类型更改为:
  • 未知类型?,包含所有可能的类型,即期望提供的收集器可能具有任何类型的可变容器(这就是JDK中所有收集器的声明方式);
  • 或上界通配符? extends Object(这基本上是描述未知类型的一种更详细的方式)。
public static Collector<Letter, ?, LetterCount> countingLetters()

最新更新