Safe Navigation Operator is released as a part of Winter 21 update.

The main advantange of Safe Navigation Operator is avoid cluttered code when there are too many null checks and make it look lot Β more verbose.

Safe Navigation Operator is not something new that's introduced in Apex, most of the Object Oriented Programming languages has this concept.

Let's look at an example where we might come across a Null Pointer Exception and I will show you how to use Safe Navigation Operator to make the code lot more verbose.

Here, in Step 1 am trying to fetch 5 random account records, think of that as list of records that are accumulated as a result of some business logic.

Then, in step 2 am fetching all the account records and storing them in a Map.

In Step 3, am fetching account record using the key which is going to be the account record Id.

If there exists no record and if we receive null in Step 3, In step 4 when I use the nottaion localAccount.Site.substring(8) it is going to give us Null Pointer Exception.

Likewise, assume we get back a record in Step 3 but there exists no value for the field site in the received account record, in that case we might again encounter Null Pointer Exception because we are using substring() method on null value.

List<String> filteredAccountSites = new List<String>();

//Step: 1
List<Account> collectedAccounts = [SELECT Id, Name, Site FROM Account LIMIT 5];

//Step: 2
Map<Id, Account> accountsMap = new Map<Id, Account>([SELECT Id, Name, Site FROM Account]);

for(Account a : collectedAccounts){
	
    //Step: 3	
    Account localAccount = accountsMap.get(a.Id);
    
    //Step: 4
    //this line will generate Null Pointer Exception when there is an 
    //account record which doesn't have value for site field. 
    
    //Because we are invoking substring() method on null and it fires 
    //up a Null Pointer Exception. 
    filteredAccountSites.add(localAccount.Site.substring(8));

}

System.debug(' πŸš€ ' +filteredAccountSites);
This is how the basic code is going to look like!

To encounter that, we need to refactor the code a bit and it's going to look like this

List<String> filteredAccountSites = new List<String>();

//Step: 1
List<Account> collectedAccounts = [SELECT Id, Name, Site FROM Account LIMIT 5];

//Step: 2
Map<Id, Account> accountsMap = new Map<Id, Account>([SELECT Id, Name, Site FROM Account]);

for(Account a : collectedAccounts){
	
    //Step: 3	
    Account localAccount = accountsMap.get(a.Id);
    if(localAccount != null){
    	if(localAccount.site != null){
        	filteredAccountSites.add(localAccount.Site.substring(8));
        }
    }

}

System.debug(' πŸš€ ' +filteredAccountSites);
Code with added null checks

But, don't you think Step 3 and 4 are looking a bit clutterd?

Here comes Safe Navigation Operator to our rescue. Look at the refactored code below.

List<String> filteredAccountSites = new List<String>();

List<Account> collectedAccounts = [SELECT Id, Name, Site FROM Account LIMIT 5];

Map<Id, Account> accountsMap = new Map<Id, Account>([SELECT Id, Name, Site FROM Account]);

for(Account a : collectedAccounts){

    filteredAccountSites.add(accountsMap.get(a.Id)?.Site?.Substring(8));

}

System.debug(' πŸš€ ' +filteredAccountSites);
Refactored code using Safe Navigation Operator

Have an extra pair of eyes on this filteredAccountSites.add(accountsMap.get(a.Id)?.Site?.Substring(8));

This looks lot more easy on this eyes, isn't it?

Basically, the notation to the left of the ?, if it's not null then the actual data will be returned, on top of which we can invoke other functions.

In case the notation to the left of ? is null, then it returns null. Thus avoiding the Null Pointer Exception.