Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@MatusMak
Last active November 29, 2019 22:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MatusMak/92fab1f2e1b20434607f9f779de7d36b to your computer and use it in GitHub Desktop.
Save MatusMak/92fab1f2e1b20434607f9f779de7d36b to your computer and use it in GitHub Desktop.
Working in Java with nulls. There is no easy and straightforward way of working with nulls in Java in comparison to other languages, like Kotlin or C#. This is my thought experiment on how to solve this with a very simple helper methods.

IMPORANT! This is my thought experiment, which may or may not be valid. Feel free to comment and give your insights, but think twice before using any of my custom solutions in your code! Always prefer using language's functionality if possible.


There are specific use cases where you always have to check if a variable is null, for example, when working with database:

Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;

try {
    connection = connect();
    statement = connection.preparedStatement(...);
    resultSet = statement.executeQuery();
} catch (SQLException e) {
    ... 
} finally {
    if (resultSet != null) {
        resultSet.close();
    }
    
    if (statement != null) {
        statement.close();
    }
    
    if (connection != null) {
        connection.close();
    }
}

This might get even more complicated if you are executing multiple statements or opening connections to multiple databases. In some languages, like Kotlin or C#, you can resolve this by using safe call operator ?.:

resultSet?.close();
statement?.close();
connection?.close();

You can apply this for getters or fields:

String carName = car?.getName();
String carMY = car?.modelYear;

In Java, using built-in libraries, you can achieve similar result with Optional.ofNullable method:

Optional.ofNullable(resultSet).ifPresent(ResultSet::close);
Optional.ofNullable(statement).ifPresent(PreparedStatement::close);
Optional.ofNullable(connection).ifPresent(Connection::close);

However, getting values back is a bit more verbose:

String name = Optional.OfNullable(car).map(Car::getName).orElse("N/A");
String modelYear = Optional.OfNullable(car).map(car -> car.modelYear).orElse("N/A");

PHP is another language where safe call operator does not exist. In Laravel, full stack web framework, there is a helper function called optional, which allows you to handle nulls like this:

optional($connection)->close();
$carName = optional($car)->getName();
$carMY = optional($car)->modelYear;
$affordable = optional($car, fn($car) => $car->price < 20000);

I really like this idea, so I created helper static methods that mimic this functionality in Java:

ObjectUtils.optional(connection, Connection::close);
String name = ObjectUtils.optional(car, Car::getName, "N/A");
String modelYear = ObjectUtils.optional(car, car -> car.modelYear, "N/A");
boolean affordable = ObjectUtils.optional(car, car -> car.price < 20000, false);

If you choose to import these helpers statically, you get almost exactly what Laravel offers:

optional(connection, Connection::close);
String name = optional(car, Car::getName, "N/A");
String modelYear = optional(car, car -> car.modelYear, "N/A");
boolean affordable = optional(car, car -> car.price < 20000, false);

What I personally like about this is that unlike built-in Java's Optional, this is a much shorter and consistent solution, and definitely more readable - Java's way feels like a hack.

Here is an implementation of my thought experiment:

public class ObjectUtils {
    
    /**
     * Executes callback for a given variable if variable
     * is not null. Otherwise, does nothing.
     * 
     * @param  T           variable      nullable variable
     * @param  Consumer<T> callback      action to be executed if variable is not null
     * @return void
     */
    public static <T> void optional(T variable, Consumer<T> callback) {
        if (variable != null) {
            callback.accept(variable);
        }
    }

    /**
     * Executes callback for a given variable if variable is
     * not null and returns result. Otherwise, returns
     * specified default value.
     * 
     * @param  T                variable      nullable variable
     * @param  Function<T, K>   callback      action to be executed if variable is not null
     * @param  K                def           default value to be returned if variable is null
     * @return result of callback call if variable is not null, def otherwise
     */
    public static <T, K> K optional(T variable, Function<T, K> callback, K def) {
        return optional(variable, callback, () -> def);
    }
    
    /**
     * Executes callback for a given variable if variable is
     * not null and returns result. Otherwise, executes and
     * returns result of specified default value supplier.
     * 
     * @param  T                variable      nullable variable
     * @param  Function<T, K>   callback      action to be executed if variable is not null
     * @param  Supplier<K>      def           default value supplier to be executed if variable is null
     * @return result of callback call if variable is not null, result of def call otherwise
     */
    public static <T, K> K optional(T variable, Function<T, K> callback, Supplier<K> def) {
        return variable != null ? callback.apply(variable) : def.get();
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment