The Apache Commons libraries are among the most popular third-party libraries for Java applications. Commons Lang is the library within the suite which adds many helper methods for the core of Java SE. Many developers are familiar with parts of the library, but are likely not familiar with the breadth of useful components in the library.
This article is not a comprehensive review of all of the methods of the
library; the Javadocs
provide that level of detail. Instead, this article exposes to the reader
many of the useful tools within the library, specifically those within
subpackages of org.apache.commons.lang
. The tools within
org.apache.commons.lang
were covered in an earlier article.
Commons Lang uses the open source Apache License and is available for download from http://commons.apache.org/downloads/download_lang.cgi.
The contracts for equals(Object
obj)
and hashCode()
are very important for correct operation of many parts of the Java language,
particularly in specifying correct behavior for collections and hashes.
Unfortunately, though, the contracts are non-trivial. Additionally,
implementing compareTo(T
o)
for Comparable classes adds another contract that has
dependencies on equals()
that requires significant forethought
to implement correctly. Joshua Bloch discusses many of the details of these
contracts in Items 8, 9, and 12 of his book Effective
Java Second Edition.
In many cases, the easiest way to generate useful and valid implementations of these methods is to use EqualsBuilder. HashCodeBuilder, and CompareToBuilder. Let's examine them with a simple example:
package com.ociweb.jnb.oct2009; import java.util.Arrays; public class BuilderExample1 implements Comparable<BuilderExample1> { private int id; private String[] names; @Override public boolean equals(Object o) { if (this == o) { return true; } if ((o == null) || (getClass() != o.getClass())) { return false; } BuilderExample1 rhs = (BuilderExample1) o; if (id != rhs.id) { return false; } if (!Arrays.equals(names, rhs.names)) { return false; } return true; } @Override public int hashCode() { int result; result = id; result = (31 * result) + ((names != null) ? Arrays.hashCode(names) : 0); return result; } public int compareTo(BuilderExample1 o) { if (id < o.id) { return -1; } else if (id < o.id) { return 1; } else { if (names.length != o.names.length) { return (names.length < o.names.length) ? -1 : +1; } else { for (int i = 0; i < names.length; i++) { String name = names[i]; String otherName = o.names[i]; final int nameCompare = name.compareTo(otherName); if (nameCompare != 0) { return nameCompare; } } } return 0; } } }
This is a fairly simple class with just two members, but proper equals()
and compareTo()
implementations take 25 lines. Here's an equally
valid implementation using EqualsBuilder
,
HashCodeBuilder
, and CompareToBuilder
:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.builder.CompareToBuilder; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; public class BuilderExample2 implements Comparable<BuilderExample2> { private int id; private String[] names; @Override public boolean equals(Object o) { return EqualsBuilder.reflectionEquals(this, o); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } public int compareTo(BuilderExample2 o) { return CompareToBuilder.reflectionCompare(this, o); } }
This implementation is much simpler than a hand-coded version, and it may be the shortest possible valid implementation. Unfortunately, because this approach uses reflection, it might not work under certain security manager settings, and it may be less efficient than explicit checking.
There is another approach that resolves these two issues without adding too much more additional code:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.builder.CompareToBuilder; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; public class BuilderExample3 implements Comparable<BuilderExample3> { private int id; private String[] names; @Override public boolean equals(Object o) { if (this == o) { return true; } if ((o == null) || (getClass() != o.getClass())) { return false; } BuilderExample3 rhs = (BuilderExample3) o; return new EqualsBuilder().append(id, rhs.id).append(names, rhs.names) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder().append(id).append(names).toHashCode(); } public int compareTo(BuilderExample3 o) { return new CompareToBuilder().append(id, o.id).append(names, o.names) .toComparison(); } }
This approach requires manually checking for nulls, sameness, and class
compatibility in equals()
, which is otherwise handled
automatically by the reflection approach. So, it's more complicated than the
reflection version, but still is much less complicated than the hand-coded
version.
Using this approach, you may choose to exclude fields that do not make up part of the essential state of an object. For example, if BuilderExample instances with the same names should be considered equal even if the ids differ, then simply do not append the id to the two builders. Such behavior is also possible through overloaded versions of the reflection methods. There are several other overloaded versions of these methods that allow different behavior for considering superclass state, applying custom primes for hashCode calculation, etc.
The other builder included in this package is the ToStringBuilder,
which can be used to create useful implementations of toString()
.
Here are two possible implementations of toString()
using this
builder. The first one is short and uses reflection (so may not work because
of security settings and may be less efficient), and the second is more
explicit.
@Override public String toString() { return ToStringBuilder.reflectionToString(this); } @Override public String toString() { return new ToStringBuilder(this).append(id).append("names", names) .toString(); }
As with the other Builders discussed above, there are several other overloaded versions of these methods that allow different behavior for considering superclass state, handling transients, etc. Additionally, the format of the output can be modified by changing the ToStringBuilder's ToStringStyle. There are five given implementations, and you can easily implement your own if they are insufficient. The following example shows how the second version above is implemented in each of the supplied ToStringStyles. Note that some of the ToStringStyles observe the request to include the field name "names" and others explicitly ignore it.
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public class BuilderExample4 { private final int id; private final String[] names; private final ToStringStyle toStringStyle; public BuilderExample4(int id, String[] names, ToStringStyle toStringStyle) { this.id = id; this.names = names; this.toStringStyle = toStringStyle; } @Override public String toString() { return new ToStringBuilder(this, toStringStyle).append(id).append( "names", names).toString(); } public static void main(String[] args) { final String[] array = {"a", "b"}; System.out.println("DEFAULT_STYLE: " + new BuilderExample4(1, array, ToStringStyle.DEFAULT_STYLE)); System.out.println("MULTI_LINE_STYLE: " + new BuilderExample4(2, array, ToStringStyle.MULTI_LINE_STYLE)); System.out.println("NO_FIELD_NAMES_STYLE: " + new BuilderExample4(3, array, ToStringStyle.NO_FIELD_NAMES_STYLE)); System.out.println("SHORT_PREFIX_STYLE: " + new BuilderExample4(4, array, ToStringStyle.SHORT_PREFIX_STYLE)); System.out.println("SIMPLE_STYLE: " + new BuilderExample4(5, array, ToStringStyle.SIMPLE_STYLE)); } } > DEFAULT_STYLE: com.ociweb.jnb.oct2009.BuilderExample4@19106c7[1,names={a,b}] > MULTI_LINE_STYLE: com.ociweb.jnb.oct2009.BuilderExample4@540408[ > 2 > names={a,b} > ] > NO_FIELD_NAMES_STYLE: com.ociweb.jnb.oct2009.BuilderExample4@1d4c61c[3,{a,b}] > SHORT_PREFIX_STYLE: BuilderExample4[4,names={a,b}] > SIMPLE_STYLE: 5,{a,b}
Note that the output will vary for each run because the last part of each
toString()
represents the unsigned hexadecimal representation
of the hash code of the object, and we haven't implemented hashCode()
to keep it consistent.
Commons Lang includes a enum implementation that is an alternative to Java's built-in Enum type. For the most part, this is a historical artifact from the days when Java did not yet have an integer enum. However, there are still at least two uses for this implementation that cannot be satisfied by Java's enum:
Here is an example of an enum implemented using Java's enum and then reimplemented using Commons Lang's Enum:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.enums.Enum; import java.util.List; public class EnumExample1 { // Plus and Minus enum written with Java's enum public enum JavaOperator { PLUS { @Override public int calculate(int operand1, int operand2) { return operand1 + operand2; } }, MINUS { @Override public int calculate(int operand1, int operand2) { return operand1 - operand2; } }; public abstract int calculate(int operand1, int operand2); } // Plus and Minus enum written with Commons' Enum public static abstract class CommonsOperator extends Enum { public static final CommonsOperator PLUS = new CommonsOperator("Plus") { @Override public int calculate(int operand1, int operand2) { return operand1 + operand2; } }; public static final CommonsOperator MINUS = new CommonsOperator( "Minus") { @Override public int calculate(int operand1, int operand2) { return operand1 + operand2; } }; private CommonsOperator(String name) { super(name); } public abstract int calculate(int operand1, int operand2); @Override public Class getEnumClass() { // this method is needed because the class has an abstract method return CommonsOperator.class; } public static List getEnumList() { return getEnumList(CommonsOperator.class); } } }
Note first that CommonsOperator takes a lot more coding than JavaOperator does. This means that it's still preferable to use Java's enum construct when possible.
Also note that CommonsOperator overrides getEnumClass()
. This is
necessary because the instances of this CommonsOperator are anonymous inner
classes, so they technically are not of type
CommonsOperator.class
. Without overriding
getEnumClass()
, getEnumList()
would not return any
instances.
Even for projects that use a more recent version of Java than 1.4, there is a reason one might use the Commons Lang Enum instead of the Java Enum. Java Enums have a restriction that Commons Lang Enums do not share: inheritance is disallowed. For example, the following hierachy is not possible using Java enums:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.enums.Enum; public class EnumExample2 { public static abstract class AbstractVehicle extends Enum { private double topSpeed; protected AbstractVehicle(String name, double topSpeed) { super(name); this.topSpeed = topSpeed; } public double getTopSpeed() { return topSpeed; } } public static class Car extends AbstractVehicle { public static final Car FORD_MODEL_T = new Car("Model T", 45.0); public static final Car TESLA_ROADSTER = new Car("Roadster", 125.0); private Car(String name, double speed) { super(name, speed); } } public static class Plane extends AbstractVehicle { public static final Plane CESSNA_CITATION_X = new Plane("Citation X", 703.0); public static final Plane BELL_X1 = new Plane("X-1", 957.0); private Plane(String name, double speed) { super(name, speed); } } }
In this example, there are two children of AbstractVehicle that inherit its
behavior. For a simple case like this, AbstractVehicle could be an interface
that a bunch of Java Enum implementations could implement, each
implementing getTopSpeed()
independently. While that would not
be too painful for this simple example, using the Java Enum might be more of
a hassle than the Commons Lang Enum if the concrete implementations share a lot
of code.
There is an entire Commons Math library devoted to scientific and statistical mathematics, there's also a small math subpackage of Commons Lang with utilities for business mathematical use.
Fraction is a useful immutable implementation of Number that supplies the ability to calculate, store, and reduce fractions. Here is a small sample of what can be done with it:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.math.Fraction; public class MathExample1 { public static void main(String[] args) { System.out.println("Fraction.FOUR_FIFTHS = " + Fraction.FOUR_FIFTHS); System.out.println("Fraction.getFraction(3, 4) = " + Fraction.getFraction(3, 4)); System.out.println("Fraction.getFraction(1, 3, 4) = " + Fraction.getFraction(1, 3, 4)); System.out.println(); // the denominator maxes at 10,000, so the second one will be 4/3 System.out.println("Fraction.getFraction(1.3333) = " + Fraction.getFraction(1.3333)); System.out.println("Fraction.getFraction(1.33333) = " + Fraction.getFraction(1.33333)); System.out.println("Fraction.getFraction(\"3.142857\") = " + Fraction.getFraction("3.142857")); System.out.println("Fraction.getFraction(Math.PI) = " + Fraction.getFraction(Math.PI)); System.out.println(); // reducing fractions System.out.println("Fraction.getFraction(6, 8) = " + Fraction.getFraction(6, 8)); System.out.println("Fraction.getReducedFraction(6, 8) = " + Fraction.getReducedFraction(6, 8)); System.out.println(); // resolves proper parts of fractions System.out.println( "Fraction.getFraction(2345, 1234).getProperNumerator() = " + Fraction.getFraction(2345, 1234).getProperNumerator()); System.out.println( "Fraction.getFraction(2345, 1234).getProperWhole() = " + Fraction.getFraction(2345, 1234).getProperWhole()); System.out.println(); // calculation System.out.println( "Fraction.FOUR_FIFTHS.divideBy(Fraction.ONE_THIRD) = " + Fraction.FOUR_FIFTHS.divideBy(Fraction.ONE_THIRD)); } } > Fraction.FOUR_FIFTHS = 4/5 > Fraction.getFraction(3, 4) = 3/4 > Fraction.getFraction(1, 3, 4) = 7/4 > Fraction.getFraction(1.3333) = 13333/10000 > Fraction.getFraction(1.33333) = 4/3 > Fraction.getFraction("3.142857") = 22/7 > Fraction.getFraction(Math.PI) = 355/113 > Fraction.getFraction(6, 8) = 6/8 > Fraction.getReducedFraction(6, 8) = 3/4 > Fraction.getFraction(2345, 1234).getProperNumerator() = 1111 > Fraction.getFraction(2345, 1234).getProperWhole() = 1 > Fraction.FOUR_FIFTHS.divideBy(Fraction.ONE_THIRD) = 12/5
NumberUtils provides some basic number utilities that enhance or extend what is available in Java's primitives and wrappers.
One example is the toXXX()
family of methods. These methods
convert a String
to Double
, Int
,
etc., which is already the behavior of Double.parseDouble(String
s), etc. However, these methods add error checking and support default
values that the built-in methods do not provide. This example uses only the
Double
versions, but analogous methods exist for Float
,
Int
, and Long
:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.math.NumberUtils; public class MathExample2 { public static void main(String[] args) { // using the default backup value System.out.println("NumberUtils.toDouble(\"1.5\") = " + NumberUtils.toDouble("1.5")); System.out.println("NumberUtils.toDouble(\"1.5r3\") = " + NumberUtils.toDouble("1.5r3")); System.out.println("NumberUtils.toDouble(\"\") = " + NumberUtils.toDouble("")); System.out.println("NumberUtils.toDouble(null) = " + NumberUtils.toDouble(null)); // specifying a backup value System.out.println("NumberUtils.toDouble(\"1.5\", 1.2) = " + NumberUtils.toDouble("1.5", 1.2)); System.out.println("NumberUtils.toDouble(\"1.5r3\", 1.2) = " + NumberUtils.toDouble("1.5r3", 1.2)); System.out.println("NumberUtils.toDouble(\"\", 1.2) = " + NumberUtils.toDouble("", 1.2)); System.out.println("NumberUtils.toDouble(null, 1.2) = " + NumberUtils.toDouble(null, 1.2)); } } > NumberUtils.toDouble("1.5") = 1.5 > NumberUtils.toDouble("1.5r3") = 0.0 > NumberUtils.toDouble("") = 0.0 > NumberUtils.toDouble(null) = 0.0 > NumberUtils.toDouble("1.5", 1.2) = 1.5 > NumberUtils.toDouble("1.5r3", 1.2) = 1.2 > NumberUtils.toDouble("", 1.2) = 1.2 > NumberUtils.toDouble(null, 1.2) = 1.2
java.lang.Math
provides max()
and min()
methods for comparing two
numbers (they are provided for long, int, short, byte, double, and float).
NumberUtils extends this with analogous versions for more than two numbers.
Unfortunately, these methods predate varargs,
so there is one overloaded version method for three arguments of each type
and one for an array of each type; it would be preferable to instead have a
single method that takes a vararg.
The subpackage also provides a suite of implementations of the Range abstract class, which can be used for defining simple bounds that can be checked in a variety of numerical types:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.math.DoubleRange; import org.apache.commons.lang.math.IntRange; public class MathExample3 { public static void main(String[] args) { final DoubleRange range = new DoubleRange(1.2, 4.3); // contains value System.out.println("range.containsDouble(1.199999999999999) = " + range.containsDouble(1.199999999999999)); System.out.println("range.containsDouble(1.2) = " + range.containsDouble(1.2)); System.out.println("range.containsDouble(1.3) = " + range.containsDouble(1.3)); System.out.println("range.containsDouble(4.3) = " + range.containsDouble(4.3)); System.out.println("range.containsDouble(4.300000000000001) = " + range.containsDouble(4.300000000000001)); System.out.println("range.containsInteger(2) = " + range.containsInteger(2)); System.out.println("range.containsInteger(5) = " + range.containsInteger(5)); // contains range System.out.println("range.containsRange(new IntRange(3, 4)) = " + range.containsRange(new IntRange(3, 4))); System.out.println("range.containsRange(new IntRange(3, 5)) = " + range.containsRange(new IntRange(3, 5))); System.out.println("range.overlapsRange(new IntRange(3, 5)) = " + range.overlapsRange(new IntRange(3, 5))); // bounds checking System.out.println("range.getMinimumDouble() = " + range.getMinimumDouble()); System.out.println("range.getMinimumLong() = " + range.getMinimumLong()); } } > range.containsDouble(1.199999999999999) = false > range.containsDouble(1.2) = true > range.containsDouble(1.3) = true > range.containsDouble(4.3) = true > range.containsDouble(4.300000000000001) = false > range.containsInteger(2) = true > range.containsInteger(5) = false > range.containsRange(new IntRange(3, 4)) = true > range.containsRange(new IntRange(3, 5)) = false > range.overlapsRange(new IntRange(3, 5)) = true > range.getMinimumDouble() = 1.2 > range.getMinimumLong() = 1
One possibly surprising result here is that the minimum Long of a range starting at 1.2 is 1. This is because that method uses a simple cast on the lower bound, which truncates the value, instead of finding a integral value within the range.
Java's combination of mutable primitives and immutable wrapper classes usually works, but one area where it doesn't work is in setting a value in a called method and having the new value visible in the caller. One way this can be a problem is in methods that value to calculate mutiple values, as in this trivial example:
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.Validate; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.apache.commons.lang.mutable.MutableDouble; import java.util.Arrays; public class MutableExample { private static Roots getSquareRootsObject(double value) { final double sqrt = Math.sqrt(value); final Roots roots = new Roots(); roots.negative = -sqrt; roots.positive = sqrt; return roots; } private static void getSquareRootsArray(double value, double[] roots) { Validate.isTrue(roots.length == 2, "The output array must have size 2", roots.length); final double sqrt = Math.sqrt(value); roots[0] = -sqrt; roots[1] = sqrt; } private static void getSquareRootsArrays(double value, double[] positive, double[] negative) { Validate.isTrue(negative.length == 1, "The output array must have size 1", negative.length); Validate.isTrue(positive.length == 1, "The output array must have size 1", positive.length); final double sqrt = Math.sqrt(value); negative[0] = -sqrt; positive[0] = sqrt; } private static void getSquareRootsMutable(double value, MutableDouble negative, MutableDouble positive) { final double sqrt = Math.sqrt(value); negative.setValue(-sqrt); positive.setValue(sqrt); } public static void main(String[] args) { // using value object System.out.println("getSquareRootsObject(6.25) = " + getSquareRootsObject(6.25)); System.out.println(); // using one array final double[] roots = new double[2]; getSquareRootsArray(6.25, roots); System.out.println("array roots = " + Arrays.toString(roots)); System.out.println(); // using multiple arrays final double[] negativeArray = new double[1]; final double[] positiveArray = new double[1]; getSquareRootsArrays(6.25, negativeArray, positiveArray); System.out.println("negativeArray = " + negativeArray[0]); System.out.println("positiveArray = " + positiveArray[0]); System.out.println(); // using mutable objects final MutableDouble negativeMutable = new MutableDouble(); final MutableDouble positiveMutable = new MutableDouble(); getSquareRootsMutable(6.25, negativeMutable, positiveMutable); System.out.println("negativeMutable = " + negativeMutable); System.out.println("positiveMutable = " + positiveMutable); // demonstrate the doubleValue method System.out.println("negativeMutable = " + negativeMutable.doubleValue()); System.out.println("positiveMutable = " + positiveMutable.doubleValue()); } private static class Roots { private double negative; private double positive; @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); } } } > getSquareRootsObject(6.25) = -2.5,2.5 > > array roots = [-2.5, 2.5] > > negativeArray = 2.5 > positiveArray = -2.5 > > negativeMutable = -2.5 > positiveMutable = 2.5 > negativeMutable = -2.5 > positiveMutable = 2.5
This example shows several different approaches for returning multiple double values from a method:
getSquareRootsObject()
getSquareRootsArray()
assert
that cannot be
disabled), and requires the caller to know the order in which the output
values were placed into the array.
getSquareRootsArrays()
getSquareRootsMutable()
getSquareRootsArrays()
without the array size or semantic misuse disadvantages.
Mutable versions of most of the primitives are provided, and each of these
classes implements Number
. Additionally, a MutableObject
is provided for wrapping general immutable objects.
In general, the time subpackage provides utilities to wrap around Java's
Date
and Calendar
APIs. If you are on a project
that must use those APIs, then the classes here, particularly DateUtils,
are likely very useful. It provides a lot of convenience methods for date
and time math and comparisons. However, if your project has the freedom to
choose an alternate date and time API, Joda
Time is probably preferable.
One utility in the time subpackage that does not have an analog in Joda is StopWatch, an implementation of a sports stopwatch. This class contains a comprehensive state machine, so asking for the split time when the stopwatch isn't split will invoke an exception.
package com.ociweb.jnb.oct2009; import org.apache.commons.lang.math.RandomUtils; import org.apache.commons.lang.time.StopWatch; public class TimeExample { public static void main(String[] args) throws InterruptedException { final StopWatch stopWatch = new StopWatch(); stopWatch.start(); System.out.println("At the start"); System.out.println("stopWatch.getStartTime() = " + stopWatch.getStartTime()); System.out.println("stopWatch.getTime() = " + stopWatch.getTime()); Thread.sleep(RandomUtils.nextInt(500)); stopWatch.split(); System.out.println("\nAfter split"); System.out.println("stopWatch.getSplitTime() = " + stopWatch.getSplitTime()); System.out.println("stopWatch.getTime() = " + stopWatch.getTime()); int pause = RandomUtils.nextInt(500); Thread.sleep(pause); System.out.println("\nAfter " + pause + " ms delay, time continues but split doesn't"); System.out.println("stopWatch.getSplitTime() = " + stopWatch.getSplitTime()); System.out.println("stopWatch.getTime() = " + stopWatch.getTime()); stopWatch.unsplit(); Thread.sleep(RandomUtils.nextInt(500)); stopWatch.suspend(); System.out.println("\nAfter suspend"); System.out.println("stopWatch.getTime() = " + stopWatch.getTime()); pause = RandomUtils.nextInt(500); Thread.sleep(pause); stopWatch.resume(); System.out.println("\nAfter " + pause + " ms delay, nothing changes"); System.out.println("stopWatch.getTime() = " + stopWatch.getTime()); Thread.sleep(RandomUtils.nextInt(500)); stopWatch.stop(); System.out.println("\nAfter stop"); System.out.println("stopWatch.getTime() = " + stopWatch.getTime()); } } > At the start > stopWatch.getStartTime() = 1243723553937 > stopWatch.getTime() = 0 > > After split > stopWatch.getSplitTime() = 375 > stopWatch.getTime() = 375 > > After 282 ms delay, time continues but split doesn't > stopWatch.getSplitTime() = 375 > stopWatch.getTime() = 656 > > After suspend > stopWatch.getTime() = 672 > > After 165 ms delay, nothing changes > stopWatch.getTime() = 672 > > After stop > stopWatch.getTime() = 1125
Note that all of these time results will vary based the machine and the particular execution.
As I said in previous article, Commons Lang provides many useful components for the general Java developer. Some of the components are clever and reduce a lot of work (like those in the builder subpackage), but a lot of the components are nothing more than nullsafe versions of methods in the JDK. However, even those simpler methods can significantly reduce the amount of boilerplate code a developer has to write.
I hope that a future version of Commons Lang makes more use of generics and varargs in order to simplify and strengthen the API, but as it is it's already a very useful resource.