How to Use Java’s Validation Methods and a Comparison of Major Libraries

This article can be read in about 33 minutes.

Introduction

Proper verification of arguments and object states is essential to ensure the reliability of a program.
To prevent bugs and unexpected behavior, it’s recommended to check input values and object states early on and to notify any issues.

Java offers libraries that make such validation efficient, with Spring, Apache Commons Lang, and Google Guava being among the most widely used.
This article provides a detailed explanation of the classes and methods for validation offered by these three libraries.

Additionally, it covers key points to consider when implementing your own validation logic.

I hope this article helps you understand how to choose and use validation methods effectively and apply them in practice.

Assert in Spring

If you are using the Spring framework, you can utilize the Assert class provided by Spring.
However, keep in mind that despite its name, Assert is not intended for use as an assertion tool in JUnit or other testing frameworks.

Below are examples of the various methods in Assert and how to use them.

Assert methods and usage examples

Method (not including extra args)DescriptionException to be thrown
isNull(T)Throws an exception if the target value is not null.IllegalArgumentException
notNull(T)Throws an exception if the target value is null.IllegalArgumentException
noNullElements(T)Throws an exception if the target array or list contains null elements.
Does not throw an exception if the array or list is null.
IllegalArgumentException
notEmpty(T)Throws an exception if the target value is empty.
Cannot be used for strings.
IllegalArgumentException
isTrue(boolean)Throws an exception if the target value is false.IllegalArgumentException
state(boolean)Throws an exception if the target value is false.
For object state verification.
IllegalStateException
hasLength(String)Throws an exception if the target string does not contain one or more arbitrary characters.IllegalArgumentException
hasText(String)Throws an exception if the target string does not contain at least one character, excluding certain special characters such as spaces.IllegalArgumentException
doesNotContain(String textToSearch, String substring)Throws an exception if textToSearch does not contain substring.IllegalArgumentException
isInstanceOf(Class type, Object obj)Throws an exception if obj is not an instance of type.
For class validation.
IllegalArgumentException
isAssignable(Class superType, Class subType)Throws an exception if subType cannot be converted to superType.
For cast validation.
IllegalArgumentException

Note that the Assert validation methods can accept an additional argument, which is an exception message to be used in case of a validation failure. If no exception message is provided, the default message defined in Assert will be used.

Below is an example of how to use Assert methods when defining your own exception message.

import static org.springframework.util.Assert.*;

isNull(user, "user must be not null");
// IllegalArgumentException: user must be not null
notNull(user, "user can not be null");
// IllegalArgumentException: user can not be null
noNullElements(list, "element can not be null");
// IllegalArgumentException: element can not be null
notEmpty(list, "list can not be empty");
// IllegalArgumentException: list can not be empty

isTrue(intArg > 0, "value must be greater than 0");
// IllegalArgumentException: value must be greater than 0
state(setting.isInitialized(), "setting is not initialized");
// IllegalStateException: setting is not initialized

hasLength(stringArg, "value can not be empty");
// IllegalArgumentException: value can not be empty
hasText(stringArg, "value must be at least 1 character");
// IllegalArgumentException: value must be at least 1 character
doesNotContain(stringArg, "abc", "value must contain substrings 'abc'");
// IllegalArgumentException: value must contain substrings 'abc'

isInstanceOf(Parent.class, child, "instance is not an parent class");
// IllegalArgumentException: instance is not an parent class
isAssignable(Parent.class, Child.class, "can not assign a Child to a Parent");
// IllegalArgumentException: can not assign a Child to a Parent

Features of Assert

1. Easily integrates with Spring

It is available for Spring applications and is attractive because you can immediately incorporate logic without needing to install additional libraries.
It may be a good option if you want to easily validate inputs and states in your Spring project.

Incidentally, the official documentation notes that “Assert is intended for internal use within the framework; for a more comprehensive suite of assertion utilities consider Apache Commons Lang’s Validate, Google Guava’s Preconditions, or similar third-party libraries.”

However, Assert provides standard methods like notNull and state, which should be more than enough for basic validation needs.
Since it is built with simple logic, there are no performance concerns.

2. Most methods throw IllegalArgumentException

Most exceptions thrown by Assert methods are IllegalArgumentException.

For example, the notNull method in Assert throws IllegalArgumentException.

However, the notNull method in Apache Commons Lang and the checkNotNull method in Google Guava (equivalent methods) throw NullPointerException.

3. Some methods do not check arguments for null

Be careful when using the noNullElements method in Assert.
The noNullElements method in Assert does not throw an exception if the array or list passed as an argument is null.

Apache Commons Lang provides an equivalent method, but its noNullElements method does throw an exception if the argument is null.

Ensure that you don’t mistakenly pass null to the noNullElements method.

Validate in Apache Commons Lang

Apache Commons Lang is a third-party library that offers a variety of general-purpose functionalities.
Apache Commons Lang provides the Validate class for argument and state validation.

Below are examples of Validate methods and how to use them.

Validate methods and usage examples

Method (not including extra args)DescriptionException to be thrown
notNull(T)Throws an exception if the target value is null.NullPointerException
noNullElements(T)Throws an exception if the target array or list contains null elements.IllegalArgumentException
notEmpty(T)Throws an exception if the target value is empty.
Can be used for lists as well as strings.
IllegalArgumentException
notBlank(T)Throws an exception if the target value is empty or contains only spaces.IllegalArgumentException
isTrue(boolean)Throws an exception if the target value is false.IllegalArgumentException
validState(boolean)Throws an exception if the target value is false.
For object state verification.
IllegalStateException
notNaN(double)Throws an exception if the target value is NaN (Not-a-Number).IllegalArgumentException
finite(double)Throws an exception if the target value is NaN or infinitely.IllegalArgumentException
inclusiveBetween(long start, long end, long value)Throws an exception if start <= value && value <= end is not satisfied.IllegalArgumentException
exclusiveBetween(long start, long end, long value)Throws an exception if start < value && value < end is not satisfied.IllegalArgumentException
validIndex(T, index)Throws an exception if index is not a valid position index for the target string.IndexOutOfBoundsException
matchesPattern(CharSequence input, String pattern)Throws an exception if input does not match the pattern (regular expression pattern).IllegalArgumentException
isInstanceOf(Class type, Object obj)Throws an exception if obj is not an instance of type.
For class validation.
IllegalArgumentException
isAssignableFrom(Class superType, Class type)Throws an exception if type cannot be converted to superType.
For cast validation.
IllegalArgumentException

Similar to Spring’s Assert, some of the Validate methods allow you to pass an exception message as an additional argument to be used in case of a validation failure.

Here is an example of how to use the Validate methods when defining your own exception message.

import static org.apache.commons.lang3.Validate.*;

notNull(user, "user can not be null");
// NullPointerException: user can not be null
noNullElements(list, "element can not be null");
// IllegalArgumentException: element can not be null
notEmpty(list, "list can not be empty");
// IllegalArgumentException: list can not be empty
notBlank(stringArg, "value can not be blank");
// IllegalArgumentException: value can not be blank

isTrue(intArg > 0, "value must be greater than 0");
// IllegalArgumentException: value must be greater than 0
validState(setting.isInitialized(), "setting is not initialized");
// IllegalStateException: setting is not initialized

notNaN(doubleArg, "value can not be NaN");
// IllegalArgumentException: value can not be NaN
finite(doubleArg, "value can not be infinite or NaN");
// IllegalArgumentException: value can not be infinite or NaN
inclusiveBetween(1, 5, 6);
// IllegalArgumentException: The value 6 is not in the specified inclusive range of 1 to 5
exclusiveBetween(1, 5, 5);
// IllegalArgumentException: The value 5 is not in the specified exclusive range of 1 to 5

validIndex("abc", 4);
// IndexOutOfBoundsException: The validated character sequence index is invalid: 4
matchesPattern("Hello!", "[a-z]*");
// IllegalArgumentException: The string Hello! does not match the pattern [a-z]*

isInstanceOf(Parent.class, child, "instance is not an parent class");
// IllegalArgumentException: instance is not an parent class
isAssignableFrom(Parent.class, Child.class, "can not assign a Child to a Parent");
// IllegalArgumentException: can not assign a Child to a Parent

Features of Validate

1. Suitable for a variety of use cases

It is similar to Spring’s Assert, but offers more functionality.
I believe Validate can be used for a wide range of purposes.

2. Methods return the values of their arguments

One difference from Spring’s Assert is that some methods in Validate return the given arguments.

For example, the notNull method in Validate is designed to return the value of its argument, allowing you to write this.value = notNull(value);.

In contrast, Spring’s Assert notNull method does not return a value (it is defined as void), so this kind of statement is not possible.

Using the notNull method from Validate should make your code simpler and more concise.

3. Exceptions thrown are not consistent

It’s important to note that some methods in the Validate class, such as notBlank, throw a NullPointerException instead of an IllegalArgumentException when the argument value is null.

The internal processing of the notBlank method is shown below.

public static <T extends CharSequence> T notBlank(final T chars, final String message, final Object... values) {
    Objects.requireNonNull(chars, toSupplier(message, values));
    if (StringUtils.isBlank(chars)) {
        throw new IllegalArgumentException(getMessage(message, values));
    }
    return chars;
}

As mentioned above, this is because Objects.requireNonNull() is used to check for null arguments.
Objects.requireNonNull() throws a NullPointerException.

So, the notBlank method throws a NullPointerException if the argument is null and an IllegalArgumentException if the argument is blank.

4. Produces longer stack traces

As mentioned above, Validate uses Objects.requireNonNull() to check for null arguments.

In contrast, Spring’s Assert and Google Guava’s Preconditions methods do not use Objects.requireNonNull(). Instead, they check for null with if (value == null).

As a result, the logging content is differ slightly.

  • Apache Commons Lang’s Validate notNull method:
Exception in thread "main" java.lang.NullPointerException: user can not be null
        at java.util.Objects.requireNonNull(Objects.java:290)
        at org.apache.commons.lang3.Validate.notNull(Validate.java:1037)
        at com.sample.Sample.main(Sample.java:10)
  • Google Guava’s Preconditions checkNotNull method:
Exception in thread "main" java.lang.NullPointerException: user can not be null
        at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:922)
        at com.sample.Sample.main(Sample.java:10)

You might notice that the notNull method in Apache Commons Lang’s Validate class adds an extra line to the stack trace because it uses Objects.requireNonNull().
However, I don’t think this minor difference is a major issue. There’s no need to be overly concerned about it.

Preconditions in Google Guava

Like Apache Commons Lang, Google Guava offers a wide range of general-purpose features.
One of these features provided by Guava is Preconditions.

Below are examples of Preconditions methods and how to use them.

Preconditions methods and usage examples

Method (not including extra args)DescriptionException to be thrown
checkNotNull(T)Throws an exception if the target value is null.NullPointerException
checkArgument(boolean)Throws an exception if the target value is false.
For method argument validation.
IllegalArgumentException
checkState(boolean)Throws an exception if the target value is false.
For object state verification.
IllegalStateException
checkElementIndex(int index, int size)Throws an exception if index is not a valid element index for the target string, array, or list.IndexOutOfBoundsException
checkPositionIndex(int index, int size)Throws an exception if index is not a valid position index for the target string, array, or list.IndexOutOfBoundsException
checkPositionIndexes(int start, int end, int size)Throws an exception if both start and end are not within the range of 0 to size.IndexOutOfBoundsException

Similar to Spring and Apache Commons Lang, some of the Preconditions validation methods allow you to pass an exception message as an additional argument to be used in case of a check failure.

import static com.google.common.base.Preconditions.*;

checkNotNull(user, "user can not be null");
// NullPointerException: user can not be null
checkArgument(intArg > 0, "value must be greater than 0");
// IllegalArgumentException: value must be greater than 0
checkState(setting.isInitialized(), "setting is not initialized");
// IllegalStateException: setting is not initialized

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));

checkElementIndex(10, list.size(), "ElementIndex");
// IndexOutOfBoundsException: ElementIndex (10) must be less than size (3)
checkPositionIndex(4, "abc".length(), "PositionIndex");
// IndexOutOfBoundsException: PositionIndex (4) must not be greater than size (3)
checkPositionIndexes(1, 4, list.size());
// IndexOutOfBoundsException: end index (4) must not be greater than size (3)

Features of Preconditions

1. Methods are simple

Compared to Spring and Apache Commons Lang, Preconditions offers fewer and simpler methods.
This simplicity is what I like about Preconditions—I find it straightforward and easy to use.

While Apache Commons Lang offers many methods, it’s often easier to work with Guava’s simpler method set than to manage the many methods in Apache Commons Lang.

2. Methods return argument values

Similar to Apache Commons Lang’s Validate, some methods in Preconditions have the feature of returning the given argument.
This allows you to write code like this.value = notNull(value);, for example.

It’s a small but useful feature to be able to check for null and assign a value in one step.

Which Validation Class Should You Use?

If you are already using Apache Commons Lang or Google Guava, I recommend using Validate or Preconditions.
With Validate, you can implement various validation processes tailored to different situations.
On the other hand, Preconditions allows for quick implementation without spending much time figuring out how to use different validation methods.

Even if you are not using these libraries, if your application is built with Spring, it’s a good idea to use Spring’s Assert.
This lets you quickly implement validation logic without needing to install additional libraries.

If your application is not a Spring application and you are not using Apache Commons Lang or Google Guava, you should implement your own validation classes.
Additionally, if you find that the methods provided by Apache Commons Lang or similar libraries are not sufficient, you can always add your own methods.
Creating custom validation methods is not very difficult.

Creating Your Own Validation Class

If your application doesn’t have a validation class, it’s a good idea to start by implementing a class with only the essential methods.

When I developed an application using Google Guava’s Preconditions class in the past, I found that 90% of the time, I relied on the following three methods:

  1. The checkNotNull method to check for null.
  2. The checkArgument method, which throws an IllegalArgumentException.
  3. The checkState method, which throws an IllegalStateException.

These three methods should generally cover most of your needs.
If you are creating a validation class from scratch, it’s a good idea to implement these three methods.

Based on the content I’ve discussed so far, I’ve created a generic validation class.

public final class Validator {

  private Validator() {}

  public static <T> T checkNotNull(T object, String errorMessage) {
    if (object == null) {
      throw new NullPointerException(errorMessage);
    }
    return object;
  }

  public static void checkArgument(boolean expression, String errorMessage) {
    if (!expression) {
      throw new IllegalArgumentException(errorMessage);
    }
  }

  public static void checkState(boolean expression, String errorMessage) {
    if (!expression) {
      throw new IllegalStateException(errorMessage);
    }
  }
}

You can also add other methods as needed.

For example, the matchesPattern method in Apache Commons Lang uses Pattern.matches internally to compile regular expressions.
From a performance perspective, it’s not ideal to call the matchesPattern method repeatedly.
If you have a fixed regular expression pattern, it’s better to compile the regular expression in the validation class in advance so it can be reused efficiently.

Below is an example of a validation method for single-byte alphanumeric characters.

public final class Validator {

  private static final Pattern ALPHANUMERIC_PATTERN = Pattern.compile("^[a-zA-Z0-9]*$");

  // ... Omitted ...

  public static CharSequence checkAlphanumeric(CharSequence input, String errorMessage) {
    if (!ALPHANUMERIC_PATTERN.matcher(input).matches()) {
      throw new IllegalArgumentException(errorMessage);
    }
    return input;
  }
}

Conclusion

  • If you are using Apache Commons Lang, use Validate. It offers many validation methods and provides flexibility to adapt to different situations.
  • If you use Google Guava, use Preconditions. It allows you to cover many scenarios with just a few methods.
  • If you are developing a Spring application, use Assert. It’s easy to use without needing additional libraries.
  • If you don’t have any of the above libraries or they don’t meet your needs, consider creating your own validation classes. In that case, it’s best to start with the essential methods and expand the functionality as your project progresses.

I hope that by understanding the features of the validation classes and methods introduced in this article, and using them appropriately, you will be able to create more robust code.

Comments