In this post, we will discuss how to use the @Cleanup annotation from Project Lombok. We will also discuss if it is wise to use this annotation and if it is better than a try-with-resources statement.
What is the @Cleanup Annotation in Lombok
The @Cleanup annotation from Project Lombok attempts to reduce boilerplate code such as try-finally statements and try-with-resources blocks.
Take for example the following code.
public void test() throws IOException{
try (InputStreamReader inputStreamReader = new InputStreamReader(System.in)) {
try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)){
//do something
}
}
}
Here, we initialize the stream readers inside try-with-resources blocks. If we are using a Java version which is older than 8, then we would have to explicitly call the .close method of these readers inside finally blocks.
Project Lombok attempts to reduce such boilerplate code by adding the @Cleanup annotation.
public void test2() throws IOException{
@Cleanup InputStreamReader testInputStreamReader = new InputStreamReader(System.in);
@Cleanup BufferedReader testBufferedReader = new BufferedReader(testInputStreamReader);
}
How does the @Cleanup annotation work?
When adding the @Cleanup annotation, the generated code will be equivalent to using a try-finally block, where the close method is called inside the finally block.
Lombok also performs a null check on the resource before closing the it.
@Cleanup OutputStream out = new FileOutputStream("blabla");
//your code
Equivalent Java code:
OutputStream out = new FileOutputStream("blabla");
try {
//your code
}
} finally {
if (out != null) {
out.close();
}
}
The scope of the @Cleanup annotation
One important thing to understand is that Lombok will close the resource only after you exit the scope in which the resource exists. For example, if you create the resource inside an “if” statement, then the resource will be closed once the if statement terminates.
If you create the resource which exists inside the scope of the whole method, then it will be closed only after the method ends.
To demonstrate this, we created two test resource classes. The classes will log a statement when the close method is called.
static class TestBufferedReader extends BufferedReader {
public TestBufferedReader(Reader in, int sz) {
super(in, sz);
}
public TestBufferedReader(Reader in) {
super(in);
}
@Override
public void close() throws IOException {
super.close();
log.info("Buffered reader close method called");
}
}
static class TestInputStreamReader extends InputStreamReader {
public TestInputStreamReader(InputStream in) {
super(in);
}
public TestInputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException {
super(in, charsetName);
}
public TestInputStreamReader(InputStream in, Charset cs) {
super(in, cs);
}
public TestInputStreamReader(InputStream in, CharsetDecoder dec) {
super(in, dec);
}
@Override
public void close() throws IOException {
super.close();
log.info("Input stream reader closed");
}
}
Now, let us test the close mechanism from Lombok.
public static final Logger log = LoggerFactory.getLogger(LombokTest.class);
@Test
public void testLombok() throws IOException{
log.info("test method started");
@Cleanup TestInputStreamReader testInputStreamReader = new TestInputStreamReader(System.in);
@Cleanup TestBufferedReader testBufferedReader = new TestBufferedReader(testInputStreamReader);
log.info("test method ended");
}
If we run the method above, we will get the following output.
15:52:35.629 [main] INFO com.example.demo.LombokTest - test method started
15:52:35.636 [main] INFO com.example.demo.LombokTest - test method ended
15:52:35.636 [main] INFO com.example.demo.LombokTest - Input stream reader closed
15:52:35.636 [main] INFO com.example.demo.LombokTest - Buffered reader close method called
15:52:35.637 [main] INFO com.example.demo.LombokTest - Input stream reader closed
Notice that the close method was called only after the program execution went out of the scope of the method. Also notice how the close method of the InputStreamReader was called twice. This tells us that Lombok will try to close all nested resources before closing the parent resources.
By default, Lombok will call the .close method of the resource. However, if your close method has another name, you can configure it by setting the value of the @Cleanup annotation.
@Cleanup("close1") TestInputStreamReader testInputStreamReader = new TestInputStreamReader(System.in);
This will call the method named .close1 in order to clean up the resource.
Lombok @Cleanup vs try with resources
So now we come to the million dollar question. Is it better to use the @Cleanup annotation from Lombok than using a try-with-resources statement?
I think that everyone will have their own opinion about this. However, here are my two cents:
- Using the annotation does not save you from writing that much boilerplate code, specially if you are using JDK 8.
- The more you use Lombok, the more it will become hard to de-Lombok your code, and the more your application will be dependent on it.
It seems that the boilerplate code savings will be more profound in JDK 7 or older JDK version programs. However, you should not be using JDK 7 or lower as these are outdated and have proven security flaws.
Ultimately, it is up to you if you choose to use this annotation from Lombok or not.
Summary
In this post, we discuss what the @Cleanup annotation from Lombok does, and we also pointed out the pros and cons of using it. It is ultimately your decision if you choose to use such an annotation or not. However, it is best to keep in mind the long term consequences of using any library or introducing a new dependencies to your project.
If you liked this post, them please subscribe to our newsletter and to our twitter feed.
Leave a Reply
You must be logged in to post a comment.