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) | Description | Exception 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) | Description | Exception 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) | Description | Exception 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:
- The
checkNotNull
method to check fornull
. - The
checkArgument
method, which throws anIllegalArgumentException
. - The
checkState
method, which throws anIllegalStateException
.
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