将原始流收集到类型化集合中?

Collecting a raw stream into a typed collection?

提问人:Anonymous 提问时间:1/9/2021 最后编辑:Anonymous 更新时间:3/10/2021 访问量:1471

问:

我正在调用一个返回原始 .我知道流中元素的类型,并希望收集到具有声明的元素类型的集合中。什么是好的,或者不那么骇人听闻的方式?Stream

最小可重复性示例

为了一个可重现的例子,假设我想调用这个方法:

@SuppressWarnings("rawtypes")
static Stream getStream() {
    return Stream.of("Example");
}

我曾希望这会起作用,但我不明白为什么它不起作用:

    List<String> stringList = getStream().map(s -> (String) s).collect(Collectors.toList());

我得到类型不匹配:无法从对象转换为列表。为什么?

解决方法

未经检查的演员表有效。为了缩小需要标记为故意未选中的位的范围,我们可以这样做

    @SuppressWarnings("unchecked")
    Stream<String> stringStream = getStream();
    List<String> stringList = stringStream.collect(Collectors.toList());

在 Java 8 和 Java 11 上,返回的列表是 ,它包含预期的元素。java.util.ArrayListString

我的搜索结果与否

我尝试了在搜索引擎上搜索 java collect raw stream 等术语。它没有给我带来任何有用的东西。有一个封闭的 Java 错误(底部的链接)提到,一旦你对原始类型进行操作,一切都会变得原始。但是这个完全原始的版本也不起作用:

    List stringList = getStream().collect(Collectors.toList());

我仍然遇到类型不匹配:无法从对象转换为列表。怎么会这样?

我们正在为 Java 8 编写代码(还有一段时间)。

我知道这个问题有一些基于意见的东西。我仍然希望你能为我提供一些事实,帮助我编写通常被认为更好的代码。

背景:Hibernate 5

我要调用的方法位于原始方法(缺少类型参数)上。我想获取流,因为我对要收集到的集合类型有特殊要求。在我的生产代码中,我正在使用(不像本问题中的示例)。顺便说一句,在写这个问题时,我找到了一种方法来说服 Hibernate 返回一个类型化流。我仍然觉得这个问题很有趣,可以发布。org.hibernate.query.Query.getResultStream()QueryQueryCollectors.toCollection()Collectors.toLlist()

链接

java java-stream 参数 generic-collections 原始类型

评论

2赞 Slaw 1/9/2021
原始类型会擦除该类型中使用的所有泛型。这是设计使然。我不认为有任何方法可以绕过不受限制的演员阵容。
0赞 Sweeper 1/9/2021
我可能遗漏了一些东西,但是如果你知道它有什么类型的东西,为什么你不能直接投射流本身呢?((Stream<String>)getStream())
0赞 Anonymous 1/9/2021
@Sweeper 这难道不是我在解决方法中隐含的吗?对我来说,这仍然是一种解决方法。我是不是太挑剔了?
1赞 Slaw 1/9/2021
@Sweeper 直接为我编译输出。javacNote: Main.java uses unchecked or unsafe operations.
5赞 Holger 1/10/2021
强制转换为 。这是在不进行未经检查的操作的情况下摆脱原始类型的唯一方法。之后,可以按预期使用。StreamStream<?>map

答:

7赞 Anonymous 1/9/2021 #1

提示在 Java Bug 记录中:一旦我使用原始类型,一切都会变得原始。 也是一个通用方法,所以它的原始版本返回 — ,不要感到惊讶。Stream.collect()Object

我调用的方法是这样声明的:Stream

    <R,A> R collect​(Collector<? super T,A,R> collector)

因此,它返回一个 ,以及它可以从传递的收集器(无论是还是一些)中收集到什么。一旦一切都变得原始,推论就不再有效。并返回.RRCollectors.toList()Collectors.toCollection()collect()Object

总而言之:泛型是好的。当我无法避免获得原始的东西时,请尽早将其转换为通用对应物。

编辑:感谢 Sweeper 和 Holger 在评论中提供的宝贵意见,这是我的首选解决方案:

    Stream<?> wildcardStream = getStream();
    List<String> stringList = wildcardStream.map(s -> (String) s)
            .collect(Collectors.toList());
    System.out.println(stringList.getClass());
    System.out.println(stringList);

Java 11 上的输出:

class java.util.ArrayList
[Example]

强制转换为(隐式或显式)删除原始类型,而无需未经检查的操作。之后可以按预期使用。而且没有必要.StreamStream<?>map()@SuppressWarnings("unchecked")

这也意味着,对于我最初的 Hibernate 场景,解决方案是在调用之前Query 对象强制转换为 a,而不是只强制转换流。然后以与上述相同的方式在流操作的 map()强制转换每个单独的对象。Query<?>getResultStream()