# Optionals in Java

## Introduction

Optional is a class introduced in Java 8 to handle null values in order to avoid the dreadful NullPointerException (NPE). 
It is a wrapper class that can be used to represent a value that may be null or not.

Before Java 8, developers had to use null checks to handle null values.

Let's say we have a method that returns an employee from a database.

```java
public static void main(String[] args) {
    var employee = getEmployee("John");
}
```

```java
public Employee getEmployee(String name) {
    Employee employee = getEmployeeByName(name);

    if (employee != null) {
        return employee;
    } else {
        return throw new IllegalArgumentException("Employee not found");
    }
}
```

This is the equivalent code with Optional.

```java
public static void main(String[] args) {
    var employee = getEmployee("John");
}
```

```java
public Optional<Employee> getEmployee(String name) {
    Optional<Employee> employee = getEmployeeByName(name);

    if (employee.isPresent()) {
        return employee.get();
    } else {
        return employee.orElseThrow(() -> new IllegalArgumentException("Employee not found"));
        // return Optional.of(new Employee());    // or return a default employee
        // return Optional.empty();               // or return an empty optional
    }
}
```

But, wait! what is the point of the Optional class if we can achieve the same result using a null check?

There is a subtle difference: when a method returns an Optional, that method is indicating that it may or may not return a value. So you have to be prepared to handle both cases.
But that is not a big improvement over the null check.

## Real improvements with Optional

The Optional API provides additional methods.

```java
public static void main(String[] args) {
    var employee = getEmployeee("John");
}
```

The `orElse()` method returns the wrapped value or the provided value as if the optional is empty. It's preferred over the `get()` and `isPresent()` methods.

```java
public Optional<Employee> getEmployeee(String name) {
    return getEmployeeByName(name)
               .orElse(new Employee());
}
```

## Chaining stream operations

Let's try to retrieve an employee age from the database using null checks.

```java
public int getEmployeeAge(String name) {
    Employee employee = getEmployeeByName(name);

    if (employee != null) {
        if (employee.getAge() != null) {
            return employee.getAge();
        } else {
            return throw new IllegalArgumentException("Employee age is null");
        }
    }
}
```

It's a bit ugly and cumbersome. More important, forgetting one null check can cause a NPE.
The code above can be simplified by chaining stream operations. 

The Optional API provides a much more robust code.

```java
public Optional<Integer> getEmployeeAge(String name) {
    Optional<Employee> optionalEmployee = getEmployeeByName(name);
    return getEmployeeByName(name)
               .map(Employee::getAge)
               .orElse(0);
}
```

We might want to throw an exception if the age is null.
```java
public Optional<Integer> getEmployeeAge(String name) {
    return getEmployeeByName(name)
               .map(Employee::getAge)
               .orElseThrow(() -> new IllegalArgumentException("Employee age is null"));
}
```

## Do not overuse Optional

Optional can also be used for method parameters, but it does not make any sense. It shoul be used only for method return values.

```java 
// Strange use of Optional
public Optional<Integer> getEmployeeAge(Optional<String> name) {
    ..................
}
```

## Bonus: how other languages handle null values

### Scala

Scala, one important JVM language, uses the `Option` class, which is a wrapper class.

```scala
def getEmployeeAge(name: String): Option[Int] = {
  val employee = getEmployeeByName(name)
  employee.map(_.age)
}
```

### Kotlin

Other languages use an operator instead of a wrapper object to handle null values.
In Kotlin the `?` is called the **safe call operator**. 

```kotlin
// In Kotlin the returned data type is put after the semicolon
fun getEmployeeAge(String name): Int? {
    val employee = getEmployeeByName(name)
    return employee?.age // similar to `orElse(null)` in Java
}
```

If you want to provide a default value, you can use the `?:` operator.

```kotlin
val age = getEmployeeAge("John") ?: 0  // similar to `orElse(0)` in Java
```

Outside the JVM world, C# also uses the `?`  or **null-conditional operator** to indicate that a variable may be null.

### `C#`

```csharp
public int? GetEmployeeAge(string name) {
    var employee = GetEmployeeByName(name);
    return employee?.Age;
}
```

## Conclusion

As we saw, Optional is not just a replacement for null checks. It is a wrapper class that can be used to represent a value that may be null or not. We can appreciate its real power when we chain stream operations.
The Optional methods **get()** and **isPresent()** are discouraged to use. So much that the language designers regret having creating them.

Other languages like **Kotlin** and **C#** use the `?` operator to handle null values. **Scala** provides a similar solution.



