MumbleCloseable
Jim Mayer
jim at pentastich.org
Fri Jun 28 22:42:03 PDT 2013
Alas, I forgot that "generate" creates an infinite sequence and so doesn't
do what I wanted in the code I sent. The same deferred open trick can be
done with a Spliterator or Iterator though. I tried the following against
a month-old OpenJDK 8:
private static class FileReadingSpliterator implements
Spliterator<String>, Closeable {
private final File file;
private BufferedReader br;
public FileReadingSpliterator(File file) throws IOException {
this.file = file;
if (! file.canRead()) {
throw new FileNotFoundException(file.toString());
}
}
@Override
public void close() throws IOException {
if (br != null) {
br.close();
}
}
@Override
public int characteristics() {
return Spliterator.ORDERED;
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public boolean tryAdvance(Consumer<? super String> action) {
try {
if (br == null) {
br = new BufferedReader(new FileReader(file));
}
String line = br.readLine();
if (line != null) {
action.accept(line);
return true;
} else {
return false;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public Spliterator<String> trySplit() {
return null;
}
}
Jim
On Sat, Jun 29, 2013 at 12:36 AM, Jim Mayer <jim at pentastich.org> wrote:
> I agree with Rémi about deferring the open. I think that the following
> (completely untested) code would defer the file open into the execution
> context of the terminal method and would be resource safe. This version is
> very specific to file I/O, but I think the basic idea could be easily
> generalized.
>
> private class BufferedReaderSupplier implements Supplier<String>,
> Closeable {
> private final Path path;
> private final Charset cs;
> private BufferedReader br;
>
> public BufferedReaderSupplier(Path path, Charset cs) throws IOException {
> this.path = path;
> this.cs = cs;
> if (! Files.isReadable(path)) {
> throw new FileNotFoundException(path.toString());
> }
> }
>
> @Override
> public String get() {
> try {
> if (br == null) {
> br = Files.newBufferedReader(path, cs);
> }
> return br.readLine();
> } catch (IOException e) {
> throw new RuntimeException(e);
> }
> }
>
> @Override
> public void close() throws IOException {
> if (br != null) {
> br.close();
> }
> }
> }
>
> public static Stream<String> lines(Path path, Charset cs) throws
> IOException {
> BufferedReaderSupplier supplier = new BufferedReaderSupplier(path, cs);
> return Stream.generate(supplier).onClose(Closeable.asRunnable(supplier));
> }
>
> Jim Mayer
>
>
> On Fri, Jun 28, 2013 at 5:58 PM, Remi Forax <forax at univ-mlv.fr> wrote:
>
>> On 06/28/2013 10:20 PM, Brian Goetz wrote:
>>
>>> public static Stream<String> lines(Path path, Charset cs) throws
>>> IOException {
>>> BufferedReader br = Files.newBufferedReader(path, cs);
>>> return br.lines().onClose(Closeable.**asRunnable(br));
>>> }
>>>
>>
>> This code, is leaky, it will not call close() on the buffered reader,
>> if lines() or Closeable.asRunnable() throws an unchecked exception.
>>
>> As I said earlier, the only way to deal with that correctly is to
>> open/close the reader only in the terminal method, not before.
>>
>> Rémi
>>
>>
>
More information about the lambda-libs-spec-observers
mailing list