Do not wrap Collection in Optional

1. Introduction

Optional class was introduced in Java 8 to so as to provide a mechanism to represent whether a result is present or not. Using “null” for such use cases can cause errors.

This article is part of ongoing series of Optional. Read about it more here.

Optional is a value based class which means it may or may not contain a non-null value.

This article is part of ongoing series of Optional class. Read more about this class here.

2. Don’t wrap collections to Optional

2.1 Refactoring from returning null to returning Optional for Collection

Let us start with an example. We want all the invoice ids from all the transactions. Below is the method that does this job.

public static List<InvoiceId> invoiceIds(
		List<Transaction> transactions) {

	if (transactions == null) {
		return null;
	}

	List<InvoiceId> invoiceIds = new ArrayList<>();
	for (Transaction transaction : transactions) {
        	InvoiceId invoiceId = transaction.invoice().invoiceId();
		if (invoiceId != null) {
			invoiceIds.add(invoiceId);
		}
	}

	return invoiceIds;

}

Now, as we know Optional is used to represent whether result exists or not the above method can be modified to:

public static Optional<List<InvoiceId>> invoiceIds(
		List<Transaction> transactions) {

	if (transactions == null) {
		return Optional.empty();
	}

	List<InvoiceId> invoiceIds = new ArrayList<>();
	for (Transaction transaction : transactions) {
		InvoiceId invoiceId = transaction.invoice().invoiceId();
		if (invoiceId != null) {
			invoiceIds.add(invoiceId);
		}
	}

	return Optional.of(invoiceIds);

}

This method can be used as:

Optional<List<InvoiceId>> optInvoiceIds = invoiceIds(transactions);

List<InvoiceId> invoiceIds = optInvoiceIds.orElseThrow(
										() -> new Exception());

I strongly believe that a Collection must not be wrapped in Optional to denote if it is null or not. The simple reasoning is that Optional is a value based class and must be used as such. A Collection is not a value but it holds values/elements. Collections already act as Value Holder.

Also, the client of this API should know how to use Optional.

2.2 Returning empty and unmodifiable collection wrappers

Instead of using Optional.empty() and Optional.of(coll) we must return Collections.emptyList() and Collections.unmodifiableList(invoiceIds) as below:

public static List<InvoiceId> invoiceIds(
		List<Transaction> transactions) {

	if (transactions == null) {
		return Collections.emptyList();
	}

	List<InvoiceId> invoiceIds = new ArrayList<>();
	for (Transaction transaction : transactions) {
		InvoiceId invoiceId = transaction.invoice().invoiceId();
		if (invoiceId != null) {
			invoiceIds.add(invoiceId);
		}
	}

	return Collections.unmodifiableList(invoiceIds);

}

What are the advantage of using emptyList and unmodifiableList?

  1. The client need not know any additional APIs.
  2. The client can ignore checking the result i.e. List<InvoiceId>, if it is null or not.
  3. emptyList is immutable hence client cannot add any corrupt data into the result.
  4. unmodifiableList provides read-only access to client hence no corrupt data can creep into result. Remember if the elements in List are mutable then using unmodifiableList won’t be of more use.

3. Conclusion

Once again, Optional must be used to contain a result that is a value and not a container. Value is easy to identify, it can be Money, a number, time, date, etc. It is also not advisable to use Optional as few different places as below:

  1. Method parameters
  2. Constructor parameters
  3. DTO objects

Leave a Reply

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