Comparison of Ways to Check Preconditions in Java - Guava vs. Apache Commons vs. Spring Framework vs. Plain Java Asserts

This article focuses on preconditions which are directly part of the source code. It doesn't cover other approaches which use annotations and aspect oriented programming.

The use of preconditions, postconditions and invariants is extremely helpful to develop maintainable code. Unfortunately, we find preconditions quite seldom in real world applications. In some cases you can read correct preconditions in JavaDoc comments. The big disadvantage of JavaDoc comments are that (i) they just get outdated in cases the implementation changes and (ii) most of the developers just don't read them or read them too late.

Example Code

To compile the code you will need the following libraries:
  • commons-lang3-3.1.jar (315,805 bytes)
  • guava-14.0.1.jar (2,189,117 bytes)
  • spring-core-3.2.2.RELEASE.jar (866,788 bytes)
In the example code you may see four different ways to directly implement preconditions and the use of asserts. I can't recommend asserts to check preconditions, but you may mix asserts with the four other approaches.

File PreconditionExample.java :

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package com.sw_engineering_candies.example;

import org.apache.commons.lang3.Validate;
import org.springframework.util.Assert;
import com.google.common.base.Preconditions;

public class PreconditionExample {

	public static void fooPlainJavaValidation(String name, int start, int end) {
		// Preconditions
		if (null == name) {
			throw new NullPointerException("Name must not be null");
		}

		if (start >= end) {
			throw new IllegalArgumentException("Start (" + start + ") must be smaller than end (" + end + ")");
		}

		// Do something here ...
	}

	public static void fooSpringFrameworkAssert(String name, int start, int end) {
		// Preconditions
		Assert.notNull(name, "Name must not be null");
		Assert.isTrue(start < end, "Start (" + start + ") must be smaller than end (" + end + ")");

		// Do something here ...
	}

	public static void fooApacheCommonsValidate(String name, int start, int end) {
		// Preconditions
		Validate.notNull(name, "Name must not be null");
		Validate.isTrue(start < end, "Start (%s) must be smaller than end (%s)", start, end);

		// Do something here ...
	}

	public static void fooGuavaPreconditions(String name, int start, int end) {
		// Preconditions
		Preconditions.checkNotNull(name, "Name must not be null");
		Preconditions.checkArgument(start < end, "Start (%s) must be smaller than end (%s)", start, end);

		// Do something here ...
	}

	public static void fooPlainJavaAsserts(String name, int start, int end) {
		// Preconditions
		assert null != name : "Name must not be null";
		assert start < end : "Start (" + start + ") must be smaller than end (" + end + ")";

		// Do something here ...
	}

	public static void main(String[] args) {

		// Case 1: fooPlainJavaValidation
		System.out.println("\n==== Case 1: Plain Java Validation ==========================");
		PreconditionExample.fooPlainJavaValidation("abc", 2, 10);
		try {
			PreconditionExample.fooPlainJavaValidation("abc", 12, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooPlainJavaValidation(\"abc\", 12, 10)\n\t" + ex);
		}
		try {
			PreconditionExample.fooPlainJavaValidation(null, 2, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooPlainJavaValidation(null, 2, 10)\n\t" + ex);
		}

		// Case 2: fooSpringFrameworkAssert
		System.out.println("\n==== Case 2: Spring Framework Assert ========================");
		PreconditionExample.fooSpringFrameworkAssert("abc", 2, 10);
		try {
			PreconditionExample.fooSpringFrameworkAssert("abc", 12, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooSpringFrameworkAssert(\"abc\", 12, 10)\n\t" + ex);
		}
		try {
			PreconditionExample.fooSpringFrameworkAssert(null, 2, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooSpringFrameworkAssert(null, 2, 10)\n\t" + ex);
		}

		// Case 3: fooApacheCommonsValidate
		System.out.println("\n==== Case 3: Apache Commons Validate ========================");
		PreconditionExample.fooApacheCommonsValidate("abc", 2, 10);
		try {
			PreconditionExample.fooApacheCommonsValidate("abc", 12, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooApacheCommonsValidate(\"abc\", 12, 10)\n\t" + ex);
		}
		try {
			PreconditionExample.fooApacheCommonsValidate(null, 2, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooApacheCommonsValidate(null, 2, 10)\n\t" + ex);
		}

		// Case 4: fooGuavaPreconditions
		System.out.println("\n==== Case 4: Guava Preconditions ============================");
		PreconditionExample.fooGuavaPreconditions("abc", 2, 10);
		try {
			PreconditionExample.fooGuavaPreconditions("abc", 12, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooGuavaPreconditions(\"abc\", 12, 10)\n\t" + ex);
		}
		try {
			PreconditionExample.fooGuavaPreconditions(null, 2, 10);
		} catch (final RuntimeException ex) {
			System.out.println("fooGuavaPreconditions(null, 2, 10)\n\t" + ex);
		}

		// Case 5: fooGuavaPreconditions
		System.out.println("\n==== Case 5: Plain Java Asserts =============================");
		PreconditionExample.fooPlainJavaAsserts("abc", 2, 10);
		try {
			PreconditionExample.fooPlainJavaAsserts("abc", 12, 10);
		} catch (final AssertionError ex) {
			System.out.println("fooPlainJavaAsserts(\"abc\", 12, 10)\n\t" + ex);
		}
		try {
			PreconditionExample.fooPlainJavaAsserts(null, 2, 10);
		} catch (final AssertionError ex) {
			System.out.println("fooPlainJavaAsserts(null, 2, 10)\n\t" + ex);
		}

	}

}

Run the code with the correct VM option (-ea) to enable asserts:

Expected Output

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
==== Case 1: Plain Java Validation ==========================
fooPlainJavaValidation("abc", 12, 10)
       java.lang.IllegalArgumentException: Start (12) must be smaller than end (10)
fooPlainJavaValidation(null, 2, 10)
       java.lang.NullPointerException: Name must not be null 
==== Case 2: Spring Framework Assert ========================
fooSpringFrameworkAssert("abc", 12, 10)
       java.lang.IllegalArgumentException: Start (12) must be smaller than end (10)
fooSpringFrameworkAssert(null, 2, 10)
       java.lang.IllegalArgumentException: Name must not be null 
==== Case 3: Apache Commons Validate ========================
fooApacheCommonsValidate("abc", 12, 10)
       java.lang.IllegalArgumentException: Start (12) must be smaller than end (10)
fooApacheCommonsValidate(null, 2, 10)
       java.lang.NullPointerException: Name must not be null 
==== Case 4: Guava Preconditions ============================
fooGuavaPreconditions("abc", 12, 10)
       java.lang.IllegalArgumentException: Start (12) must be smaller than end (10)
fooGuavaPreconditions(null, 2, 10)
       java.lang.NullPointerException: Name must not be null 
==== Case 5: Plain Java Asserts =============================
fooPlainJavaAsserts("abc", 12, 10)
       java.lang.AssertionError: Start (12) must be smaller than end (10)
fooPlainJavaAsserts(null, 2, 10)
       java.lang.AssertionError: Name must not be null

Summary

Type Description Advantage Disadvantage
Plain Java Validation Check the needed condition and throw
the appropriate exception
- Works always without
  additional external
  libraries
- Needs a lot of code. 
- Message string can not
  use parameters
Guava Preconditions [2] The class Preconditions may throw
 - IllegalArgumentException 
 - NullPointerException
 - IllegalStateException
 - IndexOutOfBoundsException

- Message string can use
  parameters
- Works with GWT
 - footprint 2,189,117 bytes
Apache Commons Validate [3] The class Validate may throw
 - NullPointerException
 - IllegalArgumentException
 - IndexOutOfBoundsException


- Message string can use
  parameters
footprint 315,805 bytes
 
Spring Framework Assert [4] The class Validate may throw 
 - IllegalArgumentException
 - IllegalStateException
- Automatically part of
  applications which use
  Spring framework
 - Message string can
   not use parameters
- Just basic functionality
Plain Java
Asserts [1]
Don't use assertions to do work that
your application requires for correct
operation in business logic.

If the condition is false, the assert
throws an AssertionError with just
the message text.

Must be enabled during run time
with VM option.
- Good for checks during
  development and for
  documentation purposes
- May be a source of errors
  if business logic is part of
  asserts.

- An assertion failure will
   never throw an
   appropriate exceptions,
   everything is just
   an AssertionError.

- Message string can not
  use parameters.

Recommendations
  • Use Preconditions in your code - this is most of the time better than a lot of comments, but don't use asserts as preconditions.
  • Guava Preconditions and Apache Commons Validate class are the best choice and have almost the same functionality - just take what is more convenient in your technology stack.

References

[1] Programming With Assertions
[2] com.google.common.base.Preconditions.java
[3] org.apache.commons.lang3.Validate.java
[4] org.springframework.util.Assert.java

Find Code on GitHub

Change History

 Revision  Date  Author  Description
 1.0  Apr 4, 2013  Markus Sprunck   first version
 1.1  Apr 8, 2013  Markus Sprunck  source code now on GitHub
 1.2  Jun 21, 2013  Markus Sprunck  some smaller improvements and typos fixed

Sponsored Link