An argument for using Optional as input parameters
Tags: java, webdev, programming, discuss
Published: 2022-12-22 17:51:49

This article may be the hardest I wrote so far here. Not because it is really technical nor because it required a lot of research prior, but because it is controversial and I had to be sure that the arguments I will present here are good. Still, I know that some will not agree with me. So let’s get down to business.

_Article originally posted on my personal website under [Should we use Optional as input parameters in Java?](https://petrepopescu.tech/2021/10/an-argument-for-using-optional-as-input-parameters/)_

There is an ongoing debate in the Java community regarding Optional and if it should be used as input parameters for methods or only as return values. Most people, including big names from the Java ecosystem, advocate for using Optional only as output. Even [Sonar has a rule](https://rules.sonarsource.com/java/RSPEC-3553) that grants a “Major” severity for Optional as parameter. So, to say that my statement here is controversial may be an understatement.


[![Courses on Spring Boot and Play Framework](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/il8jpnxkdaz9pugoet27.png)](https://courses.petrepopescu.tech/)

And in most cases, I agree. The presence of an Optional parameter usually can be attributed to a lack of knowledge or to a faulty design. However, I am not as strict nor one-sided. Things are not black and white only. That is why I think that there are a limited number of scenarios where not only having an Optional input parameter is ok, but recommended.

## Our scenario

Let’s assume that you have a Datasource class that holds a list of Students. How this list is obtained is not important right now. This Student class has multiple fields, as shown in the code below.

```java
public class Student {
    private String firstName;
    private String lastName;
    private String middleName;
    private int yearOfBirth;
    private int studyYear;
}
```

It is a simple data class, nothing too fancy. For simplicity, I did not write any getters or setters, but they are there. Now, we will need to have a way to retrieve these students, so our Datasource class has a method ‘getStudentsFitleredBy()’. However, we don’t need ALL the students, only a subset: the ones that match certain criteria.

We can filter them by firstName, by lastName, by studyYear or ANY combination of these three. So, we will need to pass the needed parameters in our getStudents() method.

This is where I believe that having these parameters as Optional (in the way shown below) adds value. Let’s look at the code that returns the filtered list of students.

```java
public List<Student> getStudentsFitleredBy(Optional<String> firstName, Optional<String> lastName, Optional<Integer> studyYear) {
    Stream<Student> studentsStream = this.students.stream();
    if (firstName.isPresent()) {
        studentsStream = studentsStream.filter(student -> student.getFirstName().equals(firstName.get()));
    }
    if (lastName.isPresent()) {
        studentsStream = studentsStream.filter(student -> student.getLastName().equals(lastName.get()));
    }
    if (studyYear.isPresent()) {
        studentsStream = studentsStream.filter(student -> student.getStudyYear() == studyYear.get().intValue());
    }
    
    return studentsStream.collect(Collectors.toList());
}
```

The advantage that Optional introduces in this case is three-fold. First of all, you don’t have to write overloaded methods for each combination of desired filtering parameters. Second, if you don’t want filtering by one of the parameters, just send Optional.empty(). Third, and the most important one, you can clearly see from the method’s signature that any of the parameters can be omitted and the method will still work as intended.

[![Courses on Spring Boot and Play Framework](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/il8jpnxkdaz9pugoet27.png)](https://courses.petrepopescu.tech/)

Optional was introduced so that you can clearly see that “this method may or may not return something”. The same logic is here, it is just that we say “this method will returne students filtered by any of the optional parameters”. And I know what you are thinking now. Why not use them without an optional and do null check instead of isPresent()? And yes, this is a valid question and writing the method in that way will work. However, you will be losing clarity. As an outsider or a different developer than the one that actually implemented the method, you won’t know if all parameters are required without looking over the documentation or the code.

As for doing null checks for the Optional inside the method, I don’t think that they are needed here. If someone sends null, he is clearly using the API wrong.

If right now you are thinking that nobody writes methods like this to filter lists, you are wrong. But even so, let’s change things a bit. Instead of having a list that is being filtered, consider that this datasource class is a DAO that uses CriteriaBuilder to construct a JPA query that retrieves the information from the database. Things don’t looks as silly now, do they?

## Others that may agree with me
I could not find any example of Optional being used as input parameter in the standard Java library. Maybe there is somewhere, but for now it remains hidden to me. But I can provide one example from a well-known framework that uses it: Spring.

For Spring controllers, if you have request parameters (the ones that come after ? in the URL), you can specify them in the controller’s method handler as Optional. This way, the honest that are received in the URL will be mapped, while the others will be Optional.empty()

```java
@GetMapping("/students")
public List<Student> getStudents(@RequestParam Optional<String> firstName,
                                 @RequestParam Optional<String> lastName,
                                 @RequestParam Optional<Integer> studyYear) {
    ...
}
```

_You can have a request like this_: `/students?firstName=Petre&studyYear=2`

For that request, the lastName parameter will be mapped to Optional.empty()