As Mohammed noted, you can use try-with-resources. In this case, you want to have your own resource, and it is not really difficult to do.
Creating an auto-closeable class
First, your class should implement AutoCloseable
:
public class CaptureOutput implements AutoCloseable {
When constructing this class, you should
- store the old
System.out
,
- create a
PrintStream
to replace it (cf. Java: PrintStream to String?) and
- replace the default stream with
System.setOut()
.
Here is how we do it
public CaptureOutput() {
this.stream = new ByteArrayOutputStream();
this.out = System.out;
System.setOut(new PrintStream(stream));
}
The secret is the AutoCloseable.close()
method: you just undo your replacement there:
public void close() throws Exception {
System.setOut(this.out);
}
Finally, you need a method to retrieve the content:
public String getContent() {
return this.stream.toString();
}
Using try-with-resources
Done that, just pass the CaptureOutput
to the try
clause. The code below, for example...
public static void main(String[] args) throws Exception {
String content = null;
System.out.println("This will be printed");
try (CaptureOutput co = new CaptureOutput()) {
System.out.println("EXAMPLE");
content = co.getContent();
}
System.out.println("This will be printed, too.");
System.out.println("The content of the string is " + content);
}
...will result on this:
This will be printed
This will be printed, too.
The content of the string is EXAMPLE
Scope issues
Note that we do not call co.getContent()
at the last line. It is not possible because, unlike Python, the co
variable is scoped inside the try
clause. Once the try
block finishes, it is gone.[1] That's why we get the value from inside the block.
Not that elegant, right? A solution may be to give the BAOS to the CaptureOutput
constructor:
public CaptureOutput(ByteArrayOutputStream stream) {
this.stream = stream;
this.out = System.out;
System.setOut(new PrintStream(this.stream));
}
Now, we just use the stream later:
public static void main(String[] args) throws Exception {
System.out.println("This will be printed");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try (CaptureOutput co = new CaptureOutput(stream)) {
System.out.println("EXAMPLE");
}
System.out.println("This will be printed, too.");
System.out.println("The content of the string is " + stream.toString());
}
(Also, it is not possible to create the CaptureOutput
variable before the try
. That makes sense: AutoCloseable
objects are supposed to be "closed" after their use. What's the use of a closed file, after all? Our use case is a bit different from that, so we have to rely on alternatives.)
The full classes
And here are the full classes:
CaptureOutput.java
:
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
public class CaptureOutput implements AutoCloseable {
private ByteArrayOutputStream stream;
private PrintStream out;
public CaptureOutput(ByteArrayOutputStream stream) {
this.stream = stream;
this.out = System.out;
System.setOut(new PrintStream(this.stream));
}
public CaptureOutput() {
this(new ByteArrayOutputStream());
}
@Override
public void close() throws Exception {
System.setOut(this.out);
}
public String getContent() {
return this.stream.toString();
}
}
Main.java
:
import java.io.ByteArrayOutputStream;
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("This will be printed");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try (CaptureOutput co = new CaptureOutput(stream)) {
System.out.println("EXAMPLE");
}
System.out.println("This will be printed, too.");
System.out.println("The content of the string is " + stream.toString());
}
}