5

Python has a nice feature: the "with" statement. It's useful for making global changes that affect all code called within the statement.

e.g. you could define a class CapturePrintStatements to capture all print statements called within the "with"

with CapturePrintStatements() as c:
    print 'Stuff Done'
print 'More Stuff Done'
assert c.get_text() == 'Stuff Done'

Is there an equivalent in Java?

Peter
  • 12,274
  • 9
  • 71
  • 86

2 Answers2

10

try-with-resources is its Java equivalent, and is available in Java 7 and up.

That gives you the possibility to work with resources that need to be explicitly closed, without worrying about closing them. For the example:

Before Java7:

InputStream input = null;

try {
    input = new FileInputStream("myFile.txt");

} finally {
    if(input != null){
        input.close();
    }
}

Java 7 & up:

try(FileInputStream input = new FileInputStream("myFile.txt")) {
    // Do something with the InputStream
}

This is the try-with-resources construct. When the execution flow will go out of the try block, the FileInputStream will be closed automatically. This is due to the fact that FileInputStream implements the AutoCloseable interface.

Mohammed Aouf Zouag
  • 17,042
  • 4
  • 41
  • 67
10

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());
        }
    
    }
    
Community
  • 1
  • 1
brandizzi
  • 26,083
  • 8
  • 103
  • 158