Kotlin Stream.asSequence()现需先调用iterator()再调用的原因
问题描述
使用java.net.HttpClient发起简单GET请求,通过HttpResponse.BodyHandlers.ofLines()处理响应得到HttpResponse<Stream<String>>类型结果。kotlin.streams库原本提供了针对java.util.stream.Stream类型的asSequence()扩展方法,但当前环境下无法直接在返回的Stream实例上调用该方法,必须先调用iterator()获取Iterator实例后,才能调用asSequence()完成序列转换。
对应实现代码如下:
val client = HttpClient.newHttpClient() val request = HttpRequest.newBuilder().let { builder -> builder.uri(URI("http://localhost:8000/response.txt")) builder.setHeader("Content-Type", "application/json") builder.GET() builder.build() } val resp: HttpResponse<Stream<String>> = client.send(request, HttpResponse.BodyHandlers.ofLines()) // 原可用写法(当前失效): resp.body().asSequence().map{ resp.body().iterator().asSequence().map { println("${it.toString()}") }
变更原因
出现该写法变更的核心原因有两点:
- 依赖拆分导致扩展方法无法被识别:针对
java.util.stream.Stream的asSequence()扩展方法,在Kotlin 1.6及以上版本中从核心标准库拆分到了独立的JDK 8扩展模块中。如果构建配置没有显式引入该模块依赖,编译器无法识别Stream作为接收者的asSequence()扩展,只能匹配到核心标准库中为Iterator类型定义的asSequence()扩展,因此会强制要求先调用iterator()将Stream转为Iterator实例,才能调用对应转换方法。 - 响应流的特性导致旧写法存在逻辑缺陷:
HttpResponse.BodyHandlers.ofLines()返回的Stream是直接绑定到底层HTTP连接的一次性延迟流,旧版本中直接对该Stream调用asSequence()的实现逻辑,会在转换阶段就触发流的状态初始化,甚至隐式执行部分终端操作,导致HTTP连接被提前关闭,后续读取响应内容为空。而先调用iterator()拿到迭代器再转Sequence的写法,会将所有遍历操作延迟到实际消费元素的阶段,完全符合HTTP响应流的读取约束,因此版本迭代后旧的直接转换写法被限制使用,避免触发隐式的流关闭bug。
内容的提问来源于stack exchange,提问作者Ted
相关产品推荐
相关产品推荐

