Safe navigation in languages
As we saw in the previous post, Ruby development team introduced a safe navigation operator to get rid of painful chaining of objects, which could raise NPE in case of null object in the middle. But Ruby is not the only language that came up with this idea. Most of the languages have introduced this operator, usually called null propagation, or some other approaches to achieve the same goal.
In this post I write some snippets to demonstrate what other languages provide to prevent the exception raised by referencing to a null object.
Demonstration of the core problem again, comes below:
Person john = new Person("John", 54, new Address("Paris", "Champs-Elysee", 54));
Person joe = new Person("Joe", 54, null);
System.out.println(john.getAddress().getStreet()); // emits the street name
System.out.println(joe.getAddress().getStreet()); // raises NPE
And in case of hash, the same problem looks like that:
Map<String, Map> hsh = new HashMap<String, Map>();
// ... add some pairs
System.out.println(hsh.get("London").get("john"));
System.out.println(hsh.get("Paris").get("john")); // raises NPE
For testing I used a Person
object which embeds an Address
object which
has some fields. I would like to get back the value of a field in the embedded
object through the instance of Person
.
Java
Java 8 introduced Optional
type and method references. Combining these two
we can safely traverse over the embedded objects without fearing to get NPE.
If one of the values is null, it returns 'N/A'
by default.
String street = Optional.of(john)
.map(Person::getAddress)
.map(Address::getStreet)
.orElse("N/A");
System.out.println(street);
street = Optional.of(joe)
.map(Person::getAddress)
.map(Address::getStreet)
.orElse("N/A");
System.out.println(street);
In Java 8, a very handy method of Map
got introduced, called getOrDefault
.
In case of it looks like as follows:
System.out.println(hsh.getOrDefault("London", new HashMap<String, Integer>()).getOrDefault("john", -1));
System.out.println(hsh.getOrDefault("Paris", new HashMap<String, Integer>()).getOrDefault("john", -1));
Groovy
Groovy brings safe navigation operator as part of the language.
john = new Person("john", 54, new Address("London", "Baker", 221))
println(john.address.street) // Baker
joe = new Person("joe", 54, null)
println(joe?.address?.street) // though address is null it is not raising NPE
Here there are two options, one would go for getAt
and null propagation
operator (?.
) is applied on the returned object. Unfortunately it does
not return a default object. get
method returns its second parameter if key
does not exist.
println(cities.getAt('Paris')?.getAt('joe')?.getAt('age'))
println(cities.get('Paris', [:]).get('joe', [:]).get('age', -1))
Python
Python does not support null propagation on embedded objects, but it supports returning default values if key does not exist in the dictionary.
print(cities.get('London').get('joe'))
print(cities.get('Paris', {}).get('joe', 'N/A'))
C#
Safe navigation in C# looks like:
Console.WriteLine(john?.Address?.Street ?? "Portobello");
Console.WriteLine(joe?.Address?.Street ?? "Portobello");
The ??
operator ensures that in case of null or falsy value, the printed text
will be the right operand.
Though C# provides TryGetValue
which can be used for getting default values,
though it is not available for chaining.
Javascript
Both the dot notation and index support are available in Javascript to read fields of an object. Since objects acts as a dictionary, index support can be used to reach fields.
console.log(((joe || {}).address || {}).street || 'Portobello');
console.log(((joe || {})['address'] || {})['street'] || 'Portobello' )
In this post we have got some ideas how to handle null propagation in different languages.
I took the latest versions of the languages. If you are awaire of additional solutions either in these languages or in others, feel free to fork-add-pr!
Codes can be found: https://github.com/torokmark/safe-navigation-in-languages