如何将同时实现AutoCloseable、Iterable的类转为支持try-with-resources的Stream
问题原因
你给出的示例无法触发MyCursor.close()的核心原因是:StreamSupport.stream()生成的Stream实例,默认不会绑定底层Iterable实例的关闭逻辑。try-with-resources语句只会主动调用你在资源段声明的Stream自身的close()方法,而Stream的默认关闭逻辑不会关联到生成spliterator的MyCursor实例,此时MyCursor实例在生成spliterator后无引用绑定,自然不会触发关闭。
可行解决方案
方案1:将MyCursor也纳入try-with-resources管理
这是最简单稳妥的方案,直接把资源类实例和Stream都放在资源声明段即可,try-with-resources会按声明逆序自动关闭所有资源:
public class WorkingExample1 { public static void main(final String[] args) throws Exception { try (MyCursor cursor = new MyCursor(); Stream<String> stream = StreamSupport.stream(cursor.spliterator(), false)) { stream.forEach(System.out::println); } } private static class MyCursor implements AutoCloseable, Iterable<String> { public void close() throws Exception { System.out.println("close"); } public Iterator<String> iterator() { List<String> items = new ArrayList<>(); items.add("foo"); items.add("bar"); items.add("baz"); return items.iterator(); } } }
该方案优势是无额外逻辑封装,资源管理逻辑直观,即使后续Stream创建抛出异常,也能保证MyCursor被正常关闭。
方案2:将关闭逻辑绑定到Stream的关闭钩子
如果需要对外只暴露Stream实例、隐藏底层资源实现,可以用Stream的onClose()方法注册MyCursor的关闭逻辑:
public class WorkingExample2 { public static void main(final String[] args) { MyCursor cursor = new MyCursor(); try (Stream<String> stream = StreamSupport.stream(cursor.spliterator(), false) .onClose(() -> { try { cursor.close(); } catch (Exception e) { throw new RuntimeException(e); } })) { stream.forEach(System.out::println); } } // MyCursor实现和上述一致即可 }
该方案适合封装工具类的场景,调用方只需要按规范关闭Stream即可,不需要感知底层的MyCursor资源。注意该方案如果在Stream创建阶段(onClose执行前)抛出异常,会导致MyCursor无法被关闭,需要额外做异常兼容处理。
内容的提问来源于stack exchange,提问作者hertzsprung
相关产品推荐
相关产品推荐

