Разработчику Android легко объяснить это с помощью примера кода Java. Я пытаюсь прояснить ситуацию тем, что вы уже знаете.

Например, рассмотрим гипотетические классы Employee и Company. Компания содержит набор сотрудников. Достаточно просто для понимания. Метод addEmployee(Сотрудник) опущен для краткости.

package com.vk;
import java.util.*;
/**
 * Class representing a Company contianing employees.
 */
public class Company {
    private final List<Employee> employees;
    public Company() {
        //Create a Collection to store Employees instances
        employees = new ArrayList<Employee>();
    }
    /**
     * Search the employees collections for an Employee with      firstname. 
     * (for simplicity assume All employees have unique name)
     * @param firstName
     * @return instance of Employee
     */
     public Employee getEmployee(String firstName) {
        for(Employee employee : employees) {
            if(employee.getFirstName()
                         .equalsIgnoreCase(firstName)) {
                return employee;
            }
        }
 
        /* What this null represents? Employee does not exist */ 
        return null;
     }
}

Класс Employee представляет сотрудника компании. Для простоты поля ID сотрудника, назначение сотрудника и т. д. пропущены.

package com.vk;
/**
 * Basic Employee Class with FirstName and LastName
 */
public class Employee {
   private final String firstName;
   private final String lastName;
   public String getFirstName() {
       return firstName;
   }
   public String getLastName() {
       return lastName;
   }
   public Employee(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
}

Это старая обычная ванильная Java, ничего особенного.

Посмотрите на метод

Employee getEmployee(String firstName)

Эта подпись подразумевает, что данное имя возвращает мне сотрудника типа «Сотрудник» для данного имени. Через некоторое время мы снова посетим эту подпись.

В математических терминах это можно объяснить так: getEmployee — это функция, которая принимает имя типа String в качестве входных данных и выводит объект типа Employee из набора сотрудников. Это можно изобразить как связь имен с сотрудниками с использованием двух наборов.

Из приведенного выше рисунка видно, что в наборе сотрудников нет сотрудника по имени Мария. Как объяснить/передать это в коде? (Java возвращает null, который не принадлежит набору сотрудников, компилятор не жалуется на это). Теперь посмотрим на метод

//Always returns Employee type.(Java you are lying...)
Employee getEmployee(String firstName)

Когда в качестве имени передается mary, в коде возвращается null. что это представляет? Сотрудник с именем Мэри не существует в компании. Как null относится к набору сотрудников? НЕТ… Вы поняли… Сигнатура метода не является честной (она не содержит явных сведений о назначении функции). На языке java из сигнатуры метода трудно объяснить, что getEmployee выводит сотрудника с типом сотрудника ИЛИ сотрудника, который не существует в наборе сотрудников. (Вы можете возразить, что мы можем создать Null Object типа Employee..). Самого этого аргумента достаточно, чтобы этот тип абстракции присутствовал на уровне языка.

Теперь мы можем переписать метод на java, например. Все-таки не на уровне языка. Может быть, это будет добавлено в Java 9, 10 или 11….

Optional<Employee> getEmployee(String firstName)

Вот где сияют такие языки, как Kotlin и Swift. У них есть эта поддержка на уровне языка, так что компилятор/редактор позаботится о том, чтобы как программист вы тестировали на обнуляемость, иначе код не будет компилироваться, и заставляет вас думать о коде, который вы пишете.

Kotlin подпись того же метода:

fun getEmployee(firstName : String?) : Employee?

Быстрая подпись того же метода:

func getEmployee(firstName : String?) -> Employee?

Компилятор подчеркивает или удостоверяется, что вызывающая сторона этого метода точно знает, что возвращает этот метод. Сотрудник или ноль. Явно говорю об этом. Надеюсь, мне удастся убедить, что «Необязательный параметр добавляет достоверности сигнатуре метода». Что это значит для разработчика? эта функция на уровне языка превращает ошибку времени выполнения в ошибку времени компиляции. (NPE перехватываются во время компиляции, а не путем прохождения трассировки стека в длинных журналах производства.)

Подробнее о null от его создателя.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare