Почему BufferedReader не закрывается при получении `Stream‹String›` в попытке с ресурсами?

Читатель должен быть закрыт, когда Stream используется в попытке с ресурсами.

Учитывая это:

try(Stream<String> lines = new BufferedReader(reader).lines()) {
            return lines.map(it -> trim ? it.trim() : it)
                    .collect(Collectors.toList());
}

... ридер не закрывается??

Этот тест не проходит:

    AtomicBoolean closed = new AtomicBoolean(false);

    Reader r = new StringReader("  Line1 \n Line2") {

                @Override
                public void close() {
                    super.close();
                    closed.set(true);
                }

            };

    try(Stream<String> lines = new BufferedReader(r).lines()) {
            lines.map(it -> trim ? it.trim() : it)
                    .collect(Collectors.toList());
    }

    assertTrue("Reader was not closed.",closed.get());

person The Coordinator    schedule 02.12.2013    source источник
comment
Stream реализует Autocloseble?   -  person PM 77-1    schedule 02.12.2013
comment
Да. Все суперинтерфейсы: AutoCloseable, BaseStream‹T, Stream‹T››   -  person The Coordinator    schedule 02.12.2013


Ответы (2)


На самом деле я не использовал синтаксис try-resources. Желаю, чтобы мой ответ имел смысл.

Насколько я понимаю, автоматическое закрытие закрывает ресурс, объявленный в инструкции, и ничего больше.

Следовательно, try(Stream<String> lines = new BufferedReader(r).lines()) { просто закрывает lines, но не тот буферизованный считыватель, которому не назначена переменная.

Если вы собираетесь закрыть и буферизованный ридер, и поток (вам действительно нужно закрывать поток в любом случае?), iirc, вы можете иметь несколько строк в операторе try:

try (BufferedReader br = new BufferedReader(r);
     Stream<String> lines = br.lines()) {
    //....
}

что-то в этом роде. (Не пробовал компилировать, хотелось бы, чтобы работало :P)

person Adrian Shum    schedule 02.12.2013
comment
Ты прав. Я посмотрел на исходный код java-8, и, хотя Stream является AutoCloseable, в Reader не вызывается close(), когда он завершается. - person The Coordinator; 02.12.2013
comment
Я тоже думаю, что это разумно :) В отличие от декораторов чтения/потока, которые вы украшаете поток, для которого закрытие декоратора должно закрыть основной поток. Завершение использования потока (и его закрытие) не означает, что базовый считыватель бесполезен и должен быть закрыт. Концептуально мы можем продолжить работу над ридером и снова получить другой поток. - person Adrian Shum; 02.12.2013
comment
@SaintHill Я не думаю, что это разумно. По крайней мере, непонятно, почему он так устроен. Я подниму вопрос в списке лямбда-разработчиков. - person ZhongYu; 02.12.2013
comment
@zhong.j.yu Пожалуйста, сделайте и поделитесь тем, что вы найдете. Меня интересует официальная причина. Почему еще Stream может быть AutoCloseable? - person The Coordinator; 02.12.2013
comment
Поток может содержать некоторые базовые ресурсы. Даже если он не закрывает ридер, его все равно нужно закрыть, чтобы освободить ресурсы, относящиеся к самому потоку. Я не вижу здесь ничего двусмысленного. - person Adrian Shum; 02.12.2013
comment
@SaintHill, тема находится здесь: mail.openjdk.java .net/pipermail/lambda-dev/2013-December/ Меня не впечатлило объяснение - person ZhongYu; 03.12.2013

Хотя это не является немедленным ответом на ваш вопрос, он гарантирует последующее закрытие всех ресурсов.

Вдохновлен реализацией CharSource.lines() в Guava, в которой используется обработчик закрытия Stream.onClose(Runnable) для закрытия соответствующего BufferedReader при закрытии обработанного потока и
характеристика Stream.flatMap(...) (благодаря этому трюк)

для вызова BaseStream.close() после того, как его содержимое было помещено в этот поток.

вы можете получить поток строк, которые будут автоматически закрыты после операции терминала.

Stream<String> lines = lines(reader);
// ..


Stream<String> lines(Reader reader) {
    BufferedReader br = new BufferedReader(reader);
    Stream<String> lines = br.lines()
            .onClose(() -> {
                try {
                    br.close();
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            });
    return Stream.of(lines).flatMap(s -> s); // Autocloseable exploit of Stream.flatMap()
}
person bjmi    schedule 11.03.2021