October 2009: Highlights of Apache Commons Lang, Part 2

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, 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 EqualsBuilderHashCodeBuilder, and CompareToBuilder. Let's examine them with a simple example:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import java.util.Arrays;
  4.  
  5.  
  6. public class BuilderExample1 implements Comparable<BuilderExample1> {
  7. private int id;
  8. private String[] names;
  9.  
  10. @Override
  11. public boolean equals(Object o) {
  12.  
  13. if (this == o) {
  14. return true;
  15. }
  16.  
  17. if ((o == null) || (getClass() != o.getClass())) {
  18. return false;
  19. }
  20.  
  21. BuilderExample1 rhs = (BuilderExample1) o;
  22.  
  23. if (id != rhs.id) {
  24. return false;
  25. }
  26.  
  27. if (!Arrays.equals(names, rhs.names)) {
  28. return false;
  29. }
  30.  
  31. return true;
  32. }
  33.  
  34. @Override
  35. public int hashCode() {
  36. int result;
  37. result = id;
  38. result = (31 * result) + ((names != null) ? Arrays.hashCode(names) : 0);
  39.  
  40. return result;
  41. }
  42.  
  43. public int compareTo(BuilderExample1 o) {
  44.  
  45. if (id < o.id) {
  46. return -1;
  47. } else if (id < o.id) {
  48. return 1;
  49. } else {
  50.  
  51. if (names.length != o.names.length) {
  52. return (names.length < o.names.length) ? -1 : +1;
  53. } else {
  54.  
  55. for (int i = 0; i < names.length; i++) {
  56. String name = names[i];
  57. String otherName = o.names[i];
  58. final int nameCompare = name.compareTo(otherName);
  59.  
  60. if (nameCompare != 0) {
  61. return nameCompare;
  62. }
  63. }
  64. }
  65.  
  66. return 0;
  67. }
  68. }
  69. }

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 EqualsBuilderHashCodeBuilder, and CompareToBuilder:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.builder.CompareToBuilder;
  4. import org.apache.commons.lang.builder.EqualsBuilder;
  5. import org.apache.commons.lang.builder.HashCodeBuilder;
  6.  
  7.  
  8. public class BuilderExample2 implements Comparable<BuilderExample2> {
  9. private int id;
  10. private String[] names;
  11.  
  12. @Override
  13. public boolean equals(Object o) {
  14. return EqualsBuilder.reflectionEquals(this, o);
  15. }
  16.  
  17. @Override
  18. public int hashCode() {
  19. return HashCodeBuilder.reflectionHashCode(this);
  20. }
  21.  
  22. public int compareTo(BuilderExample2 o) {
  23. return CompareToBuilder.reflectionCompare(this, o);
  24. }
  25. }

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:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.builder.CompareToBuilder;
  4. import org.apache.commons.lang.builder.EqualsBuilder;
  5. import org.apache.commons.lang.builder.HashCodeBuilder;
  6.  
  7.  
  8. public class BuilderExample3 implements Comparable<BuilderExample3> {
  9. private int id;
  10. private String[] names;
  11.  
  12. @Override
  13. public boolean equals(Object o) {
  14.  
  15. if (this == o) {
  16. return true;
  17. }
  18.  
  19. if ((o == null) || (getClass() != o.getClass())) {
  20. return false;
  21. }
  22.  
  23. BuilderExample3 rhs = (BuilderExample3) o;
  24.  
  25. return new EqualsBuilder().append(id, rhs.id).append(names, rhs.names)
  26. .isEquals();
  27. }
  28.  
  29. @Override
  30. public int hashCode() {
  31. return new HashCodeBuilder().append(id).append(names).toHashCode();
  32. }
  33.  
  34. public int compareTo(BuilderExample3 o) {
  35. return new CompareToBuilder().append(id, o.id).append(names, o.names)
  36. .toComparison();
  37. }
  38. }

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.

  1. @Override
  2. public String toString() {
  3. return ToStringBuilder.reflectionToString(this);
  4. }
  5.  
  6. @Override
  7. public String toString() {
  8. return new ToStringBuilder(this).append(id).append("names", names)
  9. .toString();
  10. }

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.

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.builder.ToStringBuilder;
  4. import org.apache.commons.lang.builder.ToStringStyle;
  5.  
  6.  
  7. public class BuilderExample4 {
  8. private final int id;
  9. private final String[] names;
  10. private final ToStringStyle toStringStyle;
  11.  
  12. public BuilderExample4(int id, String[] names,
  13. ToStringStyle toStringStyle) {
  14. this.id = id;
  15. this.names = names;
  16. this.toStringStyle = toStringStyle;
  17. }
  18.  
  19. @Override
  20. public String toString() {
  21. return new ToStringBuilder(this, toStringStyle).append(id).append(
  22. "names", names).toString();
  23. }
  24.  
  25. public static void main(String[] args) {
  26. final String[] array = {"a", "b"};
  27. System.out.println("DEFAULT_STYLE: " +
  28. new BuilderExample4(1, array, ToStringStyle.DEFAULT_STYLE));
  29. System.out.println("MULTI_LINE_STYLE: " +
  30. new BuilderExample4(2, array, ToStringStyle.MULTI_LINE_STYLE));
  31. System.out.println("NO_FIELD_NAMES_STYLE: " +
  32. new BuilderExample4(3, array, ToStringStyle.NO_FIELD_NAMES_STYLE));
  33. System.out.println("SHORT_PREFIX_STYLE: " +
  34. new BuilderExample4(4, array, ToStringStyle.SHORT_PREFIX_STYLE));
  35. System.out.println("SIMPLE_STYLE: " +
  36. new BuilderExample4(5, array, ToStringStyle.SIMPLE_STYLE));
  37. }
  38. }
  39. > DEFAULT_STYLE: com.ociweb.jnb.oct2009.BuilderExample4@19106c7[1,names={a,b}]
  40. > MULTI_LINE_STYLE: com.ociweb.jnb.oct2009.BuilderExample4@540408[
  41. > 2
  42. > names={a,b}
  43. > ]
  44. > NO_FIELD_NAMES_STYLE: com.ociweb.jnb.oct2009.BuilderExample4@1d4c61c[3,{a,b}]
  45. > SHORT_PREFIX_STYLE: BuilderExample4[4,names={a,b}]
  46. > 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:

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:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.enums.Enum;
  4.  
  5. import java.util.List;
  6.  
  7.  
  8. public class EnumExample1 {
  9. // Plus and Minus enum written with Java's enum
  10. public enum JavaOperator {
  11. PLUS {
  12. @Override
  13. public int calculate(int operand1, int operand2) {
  14. return operand1 + operand2;
  15. }
  16. },
  17. MINUS {
  18. @Override
  19. public int calculate(int operand1, int operand2) {
  20. return operand1 - operand2;
  21. }
  22. };
  23.  
  24. public abstract int calculate(int operand1, int operand2);
  25. }
  26.  
  27. // Plus and Minus enum written with Commons' Enum
  28. public static abstract class CommonsOperator extends Enum {
  29. public static final CommonsOperator PLUS = new CommonsOperator("Plus") {
  30. @Override
  31. public int calculate(int operand1, int operand2) {
  32. return operand1 + operand2;
  33. }
  34. };
  35.  
  36. public static final CommonsOperator MINUS = new CommonsOperator(
  37. "Minus") {
  38. @Override
  39. public int calculate(int operand1, int operand2) {
  40. return operand1 + operand2;
  41. }
  42. };
  43.  
  44. private CommonsOperator(String name) {
  45. super(name);
  46. }
  47.  
  48. public abstract int calculate(int operand1, int operand2);
  49.  
  50. @Override
  51. public Class getEnumClass() {
  52. // this method is needed because the class has an abstract method
  53. return CommonsOperator.class;
  54. }
  55.  
  56. public static List getEnumList() {
  57. return getEnumList(CommonsOperator.class);
  58. }
  59. }
  60. }

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:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.enums.Enum;
  4.  
  5.  
  6. public class EnumExample2 {
  7.  
  8. public static abstract class AbstractVehicle extends Enum {
  9. private double topSpeed;
  10.  
  11. protected AbstractVehicle(String name, double topSpeed) {
  12. super(name);
  13. this.topSpeed = topSpeed;
  14. }
  15.  
  16. public double getTopSpeed() {
  17. return topSpeed;
  18. }
  19. }
  20.  
  21. public static class Car extends AbstractVehicle {
  22. public static final Car FORD_MODEL_T = new Car("Model T", 45.0);
  23. public static final Car TESLA_ROADSTER = new Car("Roadster", 125.0);
  24.  
  25. private Car(String name, double speed) {
  26. super(name, speed);
  27. }
  28.  
  29. }
  30.  
  31. public static class Plane extends AbstractVehicle {
  32. public static final Plane CESSNA_CITATION_X = new Plane("Citation X",
  33. 703.0);
  34. public static final Plane BELL_X1 = new Plane("X-1", 957.0);
  35.  
  36. private Plane(String name, double speed) {
  37. super(name, speed);
  38. }
  39.  
  40. }
  41. }

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:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.math.Fraction;
  4.  
  5.  
  6. public class MathExample1 {
  7. public static void main(String[] args) {
  8. System.out.println("Fraction.FOUR_FIFTHS = " + Fraction.FOUR_FIFTHS);
  9. System.out.println("Fraction.getFraction(3, 4) = " +
  10. Fraction.getFraction(3, 4));
  11. System.out.println("Fraction.getFraction(1, 3, 4) = " +
  12. Fraction.getFraction(1, 3, 4));
  13. System.out.println();
  14.  
  15. // the denominator maxes at 10,000, so the second one will be 4/3
  16. System.out.println("Fraction.getFraction(1.3333) = " +
  17. Fraction.getFraction(1.3333));
  18. System.out.println("Fraction.getFraction(1.33333) = " +
  19. Fraction.getFraction(1.33333));
  20. System.out.println("Fraction.getFraction(\"3.142857\") = " +
  21. Fraction.getFraction("3.142857"));
  22. System.out.println("Fraction.getFraction(Math.PI) = " +
  23. Fraction.getFraction(Math.PI));
  24. System.out.println();
  25.  
  26. // reducing fractions
  27. System.out.println("Fraction.getFraction(6, 8) = " +
  28. Fraction.getFraction(6, 8));
  29. System.out.println("Fraction.getReducedFraction(6, 8) = " +
  30. Fraction.getReducedFraction(6, 8));
  31. System.out.println();
  32.  
  33. // resolves proper parts of fractions
  34. System.out.println(
  35. "Fraction.getFraction(2345, 1234).getProperNumerator() = " +
  36. Fraction.getFraction(2345, 1234).getProperNumerator());
  37. System.out.println(
  38. "Fraction.getFraction(2345, 1234).getProperWhole() = " +
  39. Fraction.getFraction(2345, 1234).getProperWhole());
  40. System.out.println();
  41.  
  42. // calculation
  43. System.out.println(
  44. "Fraction.FOUR_FIFTHS.divideBy(Fraction.ONE_THIRD) = " +
  45. Fraction.FOUR_FIFTHS.divideBy(Fraction.ONE_THIRD));
  46. }
  47. }
  48. > Fraction.FOUR_FIFTHS = 4/5
  49. > Fraction.getFraction(3, 4) = 3/4
  50. > Fraction.getFraction(1, 3, 4) = 7/4
  51.  
  52. > Fraction.getFraction(1.3333) = 13333/10000
  53. > Fraction.getFraction(1.33333) = 4/3
  54. > Fraction.getFraction("3.142857") = 22/7
  55. > Fraction.getFraction(Math.PI) = 355/113
  56.  
  57. > Fraction.getFraction(6, 8) = 6/8
  58. > Fraction.getReducedFraction(6, 8) = 3/4
  59.  
  60. > Fraction.getFraction(2345, 1234).getProperNumerator() = 1111
  61. > Fraction.getFraction(2345, 1234).getProperWhole() = 1
  62.  
  63. > 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 DoubleInt, 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 FloatInt, and Long:

 
  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.math.NumberUtils;
  4.  
  5.  
  6. public class MathExample2 {
  7. public static void main(String[] args) {
  8. // using the default backup value
  9. System.out.println("NumberUtils.toDouble(\"1.5\") = " +
  10. NumberUtils.toDouble("1.5"));
  11. System.out.println("NumberUtils.toDouble(\"1.5r3\") = " +
  12. NumberUtils.toDouble("1.5r3"));
  13. System.out.println("NumberUtils.toDouble(\"\") = " +
  14. NumberUtils.toDouble(""));
  15. System.out.println("NumberUtils.toDouble(null) = " +
  16. NumberUtils.toDouble(null));
  17.  
  18. // specifying a backup value
  19. System.out.println("NumberUtils.toDouble(\"1.5\", 1.2) = " +
  20. NumberUtils.toDouble("1.5", 1.2));
  21. System.out.println("NumberUtils.toDouble(\"1.5r3\", 1.2) = " +
  22. NumberUtils.toDouble("1.5r3", 1.2));
  23. System.out.println("NumberUtils.toDouble(\"\", 1.2) = " +
  24. NumberUtils.toDouble("", 1.2));
  25. System.out.println("NumberUtils.toDouble(null, 1.2) = " +
  26. NumberUtils.toDouble(null, 1.2));
  27. }
  28. }
  29. > NumberUtils.toDouble("1.5") = 1.5
  30. > NumberUtils.toDouble("1.5r3") = 0.0
  31. > NumberUtils.toDouble("") = 0.0
  32. > NumberUtils.toDouble(null) = 0.0
  33. > NumberUtils.toDouble("1.5", 1.2) = 1.5
  34. > NumberUtils.toDouble("1.5r3", 1.2) = 1.2
  35. > NumberUtils.toDouble("", 1.2) = 1.2
  36. > 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:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.math.DoubleRange;
  4. import org.apache.commons.lang.math.IntRange;
  5.  
  6.  
  7. public class MathExample3 {
  8. public static void main(String[] args) {
  9. final DoubleRange range = new DoubleRange(1.2, 4.3);
  10.  
  11. // contains value
  12. System.out.println("range.containsDouble(1.199999999999999) = " +
  13. range.containsDouble(1.199999999999999));
  14. System.out.println("range.containsDouble(1.2) = " +
  15. range.containsDouble(1.2));
  16. System.out.println("range.containsDouble(1.3) = " +
  17. range.containsDouble(1.3));
  18. System.out.println("range.containsDouble(4.3) = " +
  19. range.containsDouble(4.3));
  20. System.out.println("range.containsDouble(4.300000000000001) = " +
  21. range.containsDouble(4.300000000000001));
  22. System.out.println("range.containsInteger(2) = " +
  23. range.containsInteger(2));
  24. System.out.println("range.containsInteger(5) = " +
  25. range.containsInteger(5));
  26.  
  27. // contains range
  28. System.out.println("range.containsRange(new IntRange(3, 4)) = " +
  29. range.containsRange(new IntRange(3, 4)));
  30. System.out.println("range.containsRange(new IntRange(3, 5)) = " +
  31. range.containsRange(new IntRange(3, 5)));
  32. System.out.println("range.overlapsRange(new IntRange(3, 5)) = " +
  33. range.overlapsRange(new IntRange(3, 5)));
  34.  
  35. // bounds checking
  36. System.out.println("range.getMinimumDouble() = " +
  37. range.getMinimumDouble());
  38. System.out.println("range.getMinimumLong() = " +
  39. range.getMinimumLong());
  40. }
  41. }
  42. > range.containsDouble(1.199999999999999) = false
  43. > range.containsDouble(1.2) = true
  44. > range.containsDouble(1.3) = true
  45. > range.containsDouble(4.3) = true
  46. > range.containsDouble(4.300000000000001) = false
  47. > range.containsInteger(2) = true
  48. > range.containsInteger(5) = false
  49. > range.containsRange(new IntRange(3, 4)) = true
  50. > range.containsRange(new IntRange(3, 5)) = false
  51. > range.overlapsRange(new IntRange(3, 5)) = true
  52. > range.getMinimumDouble() = 1.2
  53. > 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:

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.Validate;
  4. import org.apache.commons.lang.builder.ToStringBuilder;
  5. import org.apache.commons.lang.builder.ToStringStyle;
  6. import org.apache.commons.lang.mutable.MutableDouble;
  7.  
  8. import java.util.Arrays;
  9.  
  10.  
  11. public class MutableExample {
  12. private static Roots getSquareRootsObject(double value) {
  13. final double sqrt = Math.sqrt(value);
  14. final Roots roots = new Roots();
  15. roots.negative = -sqrt;
  16. roots.positive = sqrt;
  17.  
  18. return roots;
  19. }
  20.  
  21. private static void getSquareRootsArray(double value, double[] roots) {
  22. Validate.isTrue(roots.length == 2, "The output array must have size 2",
  23. roots.length);
  24.  
  25. final double sqrt = Math.sqrt(value);
  26. roots[0] = -sqrt;
  27. roots[1] = sqrt;
  28. }
  29.  
  30. private static void getSquareRootsArrays(double value, double[] positive,
  31. double[] negative) {
  32. Validate.isTrue(negative.length == 1,
  33. "The output array must have size 1", negative.length);
  34. Validate.isTrue(positive.length == 1,
  35. "The output array must have size 1", positive.length);
  36.  
  37. final double sqrt = Math.sqrt(value);
  38. negative[0] = -sqrt;
  39. positive[0] = sqrt;
  40. }
  41.  
  42. private static void getSquareRootsMutable(double value,
  43. MutableDouble negative,
  44. MutableDouble positive) {
  45. final double sqrt = Math.sqrt(value);
  46. negative.setValue(-sqrt);
  47. positive.setValue(sqrt);
  48. }
  49.  
  50. public static void main(String[] args) {
  51. // using value object
  52. System.out.println("getSquareRootsObject(6.25) = " +
  53. getSquareRootsObject(6.25));
  54. System.out.println();
  55.  
  56. // using one array
  57. final double[] roots = new double[2];
  58. getSquareRootsArray(6.25, roots);
  59. System.out.println("array roots = " + Arrays.toString(roots));
  60. System.out.println();
  61.  
  62. // using multiple arrays
  63. final double[] negativeArray = new double[1];
  64. final double[] positiveArray = new double[1];
  65. getSquareRootsArrays(6.25, negativeArray, positiveArray);
  66. System.out.println("negativeArray = " + negativeArray[0]);
  67. System.out.println("positiveArray = " + positiveArray[0]);
  68. System.out.println();
  69.  
  70. // using mutable objects
  71. final MutableDouble negativeMutable = new MutableDouble();
  72. final MutableDouble positiveMutable = new MutableDouble();
  73. getSquareRootsMutable(6.25, negativeMutable, positiveMutable);
  74. System.out.println("negativeMutable = " + negativeMutable);
  75. System.out.println("positiveMutable = " + positiveMutable);
  76. // demonstrate the doubleValue method
  77. System.out.println("negativeMutable = " +
  78. negativeMutable.doubleValue());
  79. System.out.println("positiveMutable = " +
  80. positiveMutable.doubleValue());
  81. }
  82.  
  83. private static class Roots {
  84. private double negative;
  85. private double positive;
  86.  
  87. @Override
  88. public String toString() {
  89. return ToStringBuilder.reflectionToString(this,
  90. ToStringStyle.SIMPLE_STYLE);
  91. }
  92. }
  93. }
  94. > getSquareRootsObject(6.25) = -2.5,2.5
  95. >
  96. > array roots = [-2.5, 2.5]
  97. >
  98. > negativeArray = 2.5
  99. > positiveArray = -2.5
  100. >
  101. > negativeMutable = -2.5
  102. > positiveMutable = 2.5
  103. > negativeMutable = -2.5
  104. > 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.

  1. package com.ociweb.jnb.oct2009;
  2.  
  3. import org.apache.commons.lang.math.RandomUtils;
  4. import org.apache.commons.lang.time.StopWatch;
  5.  
  6.  
  7. public class TimeExample {
  8. public static void main(String[] args) throws InterruptedException {
  9. final StopWatch stopWatch = new StopWatch();
  10. stopWatch.start();
  11. System.out.println("At the start");
  12. System.out.println("stopWatch.getStartTime() = " +
  13. stopWatch.getStartTime());
  14. System.out.println("stopWatch.getTime() = " + stopWatch.getTime());
  15.  
  16. Thread.sleep(RandomUtils.nextInt(500));
  17. stopWatch.split();
  18. System.out.println("\nAfter split");
  19. System.out.println("stopWatch.getSplitTime() = " +
  20. stopWatch.getSplitTime());
  21. System.out.println("stopWatch.getTime() = " + stopWatch.getTime());
  22.  
  23. int pause = RandomUtils.nextInt(500);
  24. Thread.sleep(pause);
  25. System.out.println("\nAfter " + pause +
  26. " ms delay, time continues but split doesn't");
  27. System.out.println("stopWatch.getSplitTime() = " +
  28. stopWatch.getSplitTime());
  29. System.out.println("stopWatch.getTime() = " + stopWatch.getTime());
  30. stopWatch.unsplit();
  31.  
  32. Thread.sleep(RandomUtils.nextInt(500));
  33. stopWatch.suspend();
  34. System.out.println("\nAfter suspend");
  35. System.out.println("stopWatch.getTime() = " + stopWatch.getTime());
  36. pause = RandomUtils.nextInt(500);
  37. Thread.sleep(pause);
  38. stopWatch.resume();
  39. System.out.println("\nAfter " + pause + " ms delay, nothing changes");
  40. System.out.println("stopWatch.getTime() = " + stopWatch.getTime());
  41.  
  42. Thread.sleep(RandomUtils.nextInt(500));
  43. stopWatch.stop();
  44. System.out.println("\nAfter stop");
  45. System.out.println("stopWatch.getTime() = " + stopWatch.getTime());
  46. }
  47. }
  48. > At the start
  49. > stopWatch.getStartTime() = 1243723553937
  50. > stopWatch.getTime() = 0
  51. >
  52. > After split
  53. > stopWatch.getSplitTime() = 375
  54. > stopWatch.getTime() = 375
  55. >
  56. > After 282 ms delay, time continues but split doesn't
  57. > stopWatch.getSplitTime() = 375
  58. > stopWatch.getTime() = 656
  59. >
  60. > After suspend
  61. > stopWatch.getTime() = 672
  62. >
  63. > After 165 ms delay, nothing changes
  64. > stopWatch.getTime() = 672
  65. >
  66. > After stop
  67. > 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

 

The Software Engineering Tech Trends is a monthly publication featuring emerging trends in software engineering.

Subscribe

© Copyright Object Computing, Inc. 1993, 2018. All rights reserved

secret