Highlights of Apache Commons Lang, Part 2
By Lance Finney, OCI Principal Software Engineer
October 2009
Introduction
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.
- Builder Subpackage
EqualsBuilder
,HashCodeBuilder
, andCompareToBuilder
ToStringBuilder
- Enums Subpackage
- Using Commons Lang Enum instead of Java's Enum
- Enums with Inheritance
- Math Subpackage
- Fraction
- NumberUtils
- Ranges
- Mutable Subpackage
- Time Subpackage
- Summary
Builder Subpackage
EqualsBuilder, HashCodeBuilder, and CompareToBuilder
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.
ToStringBuilder
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.
Enums Subpackage
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:
- Projects that are restricted to Java 1.4 and earlier
- Enums that need to have an inheritance hierarchy
Using Commons Lang Enum instead of 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.
Enums with Inheritance
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.
Math Subpackage
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
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
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.
Ranges
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.
Mutable Subpackage
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()
- This method uses an inner value object to contain the two output values. This is perhaps the best overall approach, but creating a value object for every complicated calculation method can result in significant overhead.
getSquareRootsArray()
- This method uses a single array to return the two output values. This method does not require a custom object, but it requires that all the return values be of the same type, requires an array of the right size (which here is validated by the Validate utility, Commons Lang's version of
assert
that cannot be disabled), and requires the caller to know the order in which the output values were placed into the array. getSquareRootsArrays()
- This method uses an array for each output value. This method also does not require a custom object, it does not require the return values to be of the same type, and it does not require the caller to know the element order, so it is preferable to the previous approach. However, it still requires arrays of the right size, and it is a semantic misue of arrays.
getSquareRootsMutable()
- This method uses a MutableDouble for each output values, very similar to how each array is used in the previous approach. So, it has the advantages of
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.
Time Subpackage
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.
Summary
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.
References
- [1] Apache Commons
http://commons.apache.org/ - [2] Commons Lang (Version 2.4 is used in this article)
http://commons.apache.org/lang/ - [3] Commons Lang Javadocs
http://commons.apache.org/lang/api-release/index.html - [4] Effective Java Second Edition
http://java.sun.com/docs/books/effective/. - [5] Enums
http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html - [6] Code Examples
jnbOct2009.zip - [7] SETT Article (July 2009): Highlights of Apache Commons Lang, Part 1
- [8] Google Guava, a new library that provides similar tools
http://code.google.com/p/guava-libraries/