Implementing Functional Validators in Java

1. Introduction

In previous article I discussed Tuple is an immutable collection or finite list of elements of same or different types. In this article, I will discuss how to implement validations using Tuples, specifically Tuple with 2 elements. This will be an interesting article as you will learn a new technique to write validations.

2. Object validation using Tuple2

Let us create a Tuple2 object.

Tuple2<String, String> stringTuple = Tuple2.of("Jon", "Snow");
String _1 = stringTuple._1;
String _2 = stringTuple._2;

Now to create a Tuple for validation, we need Boolean and String.

  • Boolean will represent a condition.
  • String will represent a message if the condition is true.

If condition is true means validator has validated the field and it doesn’t comply with our rule.

List<Tuple2<Boolean, String>> validations = new ArrayList<>();

validations.add(Tuple2.of(
					transaction == null, 
					"empty transaction"));

validations.add(Tuple2.of(
					transaction.invoice() == null, 
					"empty invoice")); 

validations.add(Tuple2.of(
					transaction.invoice().invoiceId() == null, 
					"empty invoiceid")); 
 
for (Tuple2<Boolean, String> tuple2 : validations) {     
	// if the condition is true then validation failed.
	if (tuple2._1) { 
		return false;     
	}
}

return true; 

Seems fine, isn’t it? Unfortunately, this doesn’t work. It will throw NullPointerException. The reason behind NullPointerException is that the condition in Tuple2 is evaluated and result of that evaluation goes in Tuple2. Or you say that the conditions are evaluated eagerly not lazily.

For example :

validations.add(Tuple2.of(
				transaction.invoice() == null, 
				"Invoice is null"));

If transaction object is null, the above line would blow up. What we need is to provide a condition, but it should not be evaluated eagerly. We should execute it lazily.

So how to place condition without executing them altogether? We can execute conditions one by one lazily? How can we place the conditions in Tuple2 which can be executed lazily?

3. Combining Tuple2 with Functional Interface

We can wrap the conditions in Functional interface. This will give us the power of lazy evaluation of conditions. For example :

// Condition is not yet executed it is just wrapped in Supplier
Supplier<Boolean> booleanSupplier = () -> transaction == null; 

Supplier<String> messageSupplier = () -> "Transaction is null";
 
if(booleanSupplier.get()) {
	logger.log(Level.SEVERE, messageSupplier.get());
}

Both the suppliers can be wrapped in Tuple2 as below:

Supplier<Boolean> booleanSupplier = () -> transaction == null;

Supplier<String> messageSupplier = () -> "Transaction is null";

Tuple2<Supplier<Boolean>, Supplier<String>>  tuple2 = 
				Tuple2.of(booleanSupplier, messageSupplier);

We can also add this Tuple2 in List directly:

List<Tuple2<Supplier<Boolean>, Supplier<String>>> validations 
	= new ArrayList<>();

validations.add(Tuple2.of(
					() -> transaction == null, 
					() -> "empty transaction"));

validations.add(Tuple2.of(
					() -> transaction.invoice() == null, 
					() -> "empty invoice"));

validations.add(Tuple2.of(
					() -> transaction.invoice().invoiceId() == null, 
					() -> "empty invoice id"));
 
for (
 Tuple2<Supplier<Boolean>, Supplier<String>> tuple2 : validations) {
	// if the condition is true then validation failed.
	if (tuple2._1.get()) { 
		return false;
	}
}

return true;

4. Conclusion

In this article, we saw how to use Tuple2 to write lazy validations and executing validation logic only when needed. That is all implementing Functional Validations using Tuples. What do you think of Tuples? Comment on it and let me know.


Leave a Reply

Your email address will not be published. Required fields are marked *