Нет смысла в такой конструкции, как
return foos.flatMap(foo -> {
try (var bars = foo.bars()) {
return bars;
}
}).toArray(Bar[]::new);
, поскольку она закрывает поток до того, как будет возвращен вызывающей стороне, что делает подпоток полностью непригодным для использования.
Фактически, код функции не может гарантировать, что закрытие произойдет в соответствующем месте, которое находится за пределами функции.Это, безусловно, причина, по которой разработчики API решили, что вам это не нужно, и реализация Stream позаботится о них.
Это также относится к исключительному случаю.Stream по-прежнему гарантирует, что поток будет закрыт, как только функция вернет его в Stream:
try {
IntStream.range(1, 3)
.flatMap(i -> {
System.out.println("creating "+i);
return IntStream.range('a', 'a'+i)
.peek(j -> {
System.out.println("processing sub "+i+" - "+(char)j);
if(j=='b') throw new IllegalStateException();
})
.onClose(() -> System.out.println("closing "+i));
})
.forEach(i -> System.out.println("consuming "+(char)i));
} catch(IllegalStateException ex) {
System.out.println("caught "+ex);
}
creating 1
processing sub 1 - a
consuming a
closing 1
creating 2
processing sub 2 - a
consuming a
processing sub 2 - b
closing 2
caught java.lang.IllegalStateException
Вы можете поиграть с условиями, чтобы убедиться, что построенный Stream всегда закрыт.Для элементов внешнего потока, которые не обрабатываются, поток вообще не будет.
Для такой операции потока, как .flatMap(Foo::bars)
или .flatMap(foo -> foo.bars())
, можно предположить, что однажды bars()
успешно создан иВозвращенный поток, он будет точно передан вызывающей стороне и должным образом закрыт.
Другой сценарий - это функции отображения, которые выполняют операции после создания потока, которые могут завершиться неудачей, например,
.flatMap(foo -> {
Stream<Type> s = foo.bar();
anotherOperation(); // Stream is not closed if this throws
return s;
})
* 1023.* В этом случае было бы необходимо обеспечить закрытие в исключительном случае, но только в исключительном случае:
.flatMap(foo -> {
Stream<Type> s = foo.bar();
try {
anotherOperation();
} catch(Throwable t) {
try(s) { throw t; } // close and do addSuppressed if follow-up error
}
return s;
})
, но, очевидно, вы должны следовать общему правилу, чтобы сохранить лямбды простыми, в которыхесли вам не нужна такая защита.