This article uses Log4J as a vehicle to describe Java 5 Variable Augments through a concrete example.
There are 4 versions of the same program:
Version 1: A typical use that does not wrap as is typical
Version 2: Same as version 1 but uses a convenience method in the String class
Version 3: Shows what we should do using the existing interface
Version 4: Shows a new interface using Java 5 variable arguments
Version 1
Here is a typical example of using Log4J with a simple utility class that:
Configures Log4J
Returns the logger for a provided class
Main.java
Interesting Lines
Line
Description
14
Use a utility class to retrieve a logger for my class.
16
Use the logger.
So what is wrong with this example? Specifically, something is missing. In a real application, line 16 should be wrapped in some conditional logic. Don’t worry if you’re not sure about this, it’s coming up.
Version 2
This version only introduced the use of a new utility method on the String class. Instead of writing the following:
You can use the new method String.format and C-style format strings:
Note that String.format is an example of a method whose parameters are defined using variable arguments.
Here’s the code for version 2:
Main.java
Interesting Lines
Line
Description
16 - 17
Use the String.format method to produce the same output as before.
Version 3
Version 3 adds the missing element mentioned in version 1. Namely, in both version 1 and version 2, we are formatting a String, performing string concatenation, allocating memory, etc. when it might not be necessary.
The logger might not be displaying debug-level information. If it is not, then we have wasted time and memory on String manipulation. This is a typical problem with logging. It may not seem like much but from my experience it is a big deal. In a large system, you’ll end up garbage collecting more often than necessary.
There’s an “easy” fix. You can ask the logger if the level you’re going to output is currently enabled. If it is not, then you don’t to the work:
It is ugly but it makes a big difference.
Here’s the code modified to take this into consideration:
Main.java
Interesting Lines
Line
Description
16 - 19
Now we are wrapping our calls to the logger just in case we are not currently printing debugging information.
This is ugly, error prone and a pain, right? But it really does make a big difference. What if you could make this happen automagically?
Version 4
This version has the advantages of versions 1 and 2 in terms of what you write. It also has most of the advantages of version 3 in terms of not doing work unnecessarily.
First let’s look at the use of the code:
Main.java
Interesting Lines
Line
Description
14
What? This is just back to version 2, right?
12
This was Logger, now it is ILogger.
To make this work, we first introduce an interface that uses Java 5 Variable Arguments:
ILogger.java
Next, we write a simple implementation for this interface. For now we’ll just look at one of the methods:
Interesting Lines
Line
Description
14
This method uses variable arguments. You can tell this when you see Object…. It turns out that objects is simply an array of Objects. You can write objects[0] to get the first element (assuming there are some elements in it).
15
Our wrapper performs the isDebugEnabled() check for us rather than having to do it everywhere where we use the logger.
16
This line uses the String.format method we introduced in Version 2. It was a necessary step. Notice that we pass in a variable number of arguments and we simply pass those arguments on to the String.format method.
This version was not possible before variable arguments. We might have some overhead in the passing of parameters but we have much more elegant code that does what it should without burdening the programmer.
Here is the full implementation:
LoggerImpl.java
And finally, for completeness, here are the final two files in this final example:
LoggingConfiguration.java
log4j.properties
Notice that by using variable arguments we’ve:
Reduced what a developer needs to write and still get reasonable logging performance
Moved repeated code to a single place
But it does not come without some costs. If you wrap as demonstrated in the 3rd version:
You avoid a function call and parameter passing
Java does not need to make the array of objects, which is how it passes variable arguments
If your code calls a function to calculate the output, you call the function even if you do not perform the output.
Summary
Define Variable Arguments
Calling a method that takes them
Calling a method when you already have an array of objects
Notes
The variable argument parameter must be last
You will need to look at the contents of the array of objects
This is not something you’ll use heavily. Most likely it will support non-functional requirements better than functional requirements.
Comments