If all you're concerned about is printing strings to a file, use a PrintStream
or perhaps PrintWriter
instead of the other Writer
classes. The notable feature of PrintStream
and PrintWriter
is that their printing operations don't throw IOException
. They also call toString
on objects automatically, which makes things very convenient:
public void write1(Iterable<?> objects) {
try (PrintStream ps = new PrintStream("printout.txt", "UTF-8")) {
objects.forEach(ps::println);
} catch (IOException ioe) {
// handle
}
}
If you're concerned about errors, you can call PrintStream.checkError
, although this doesn't tell you any specifics about any error that might have occurred.
The general question still stands, though, about what to do if you want to call an exception-throwing method from within a context (such as forEach
) that doesn't permit it. This is only annoying to deal with, though only moderately so. It does require some setup, however. Suppose we want to write a Consumer
that throws an IOException
. We have to declare our own functional interface:
interface IOConsumer<T> {
void accept(T t) throws IOException;
}
Now we need to write a function that converts an IOConsumer
to a Consumer
. It does this by converting any IOException
it catches into an UncheckedIOException
, an exception created for this purpose.
static <T> Consumer<T> wrap(IOConsumer<? super T> ioc) {
return t -> {
try {
ioc.accept(t);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
};
}
With these in place, we can now rewrite the original example as follows:
public void write2(Iterable<?> objects) {
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("out.txt"), "UTF-8"))) {
objects.forEach(wrap(o -> bw.write(o.toString())));
} catch (IOException|UncheckedIOException e) {
//handle exception
}
}