Newer Version Available
Safe Navigation Operator
Use the safe navigation operator (?.) to replace explicit, sequential checks for null references. This
operator short-circuits expressions that attempt to operate on a null value and returns
null instead of throwing a NullPointerException.
If the left-hand-side of the chain expression evaluates to null, the right-hand-side isn’t evaluated. Use the safe navigation operator (?.) in method, variable, and property chaining. The part of the expression that isn’t evaluated can include variable references, method references, or array expressions.
Examples
- This example first evaluates a, and
returns null if a is null. Otherwise the
return value is a.b.
1a?.b // Evaluates to: a == null ? null : a.b - This example returns null if a[x]
evaluates to null. If a[x] doesn’t
evaluate to null and aMethod() returns
null, then this expression throws a
NullPointerException.
1a[x]?.aMethod().aField // Evaluates to null if a[x] == null - This example returns null if a[x].aMethod() evaluates to null.
1a[x].aMethod()?.aField - This example indicates that the type of the expression is the same whether the
safe navigation operator is used in the expression or
not.
1Integer x = anObject?.anIntegerField; // The expression is of type Integer because the field is of type Integer - This example shows a single statement replacing a block of code that checks for
nulls.
1// Previous code checking for nulls 2String profileUrl = null; 3if (user.getProfileUrl() != null) { 4 profileUrl = user.getProfileUrl().toExternalForm(); 5}1// New code using the safe navigation operator 2String profileUrl = user.getProfileUrl()?.toExternalForm(); - This example shows a single-row SOQL query using the safe navigation operator.
1// Previous code checking for nulls 2results = [SELECT Name FROM Account WHERE Id = :accId]; 3if (results.size() == 0) { // Account was deleted 4 return null; 5} 6return results[0].Name;1// New code using the safe navigation operator 2return [SELECT Name FROM Account WHERE Id = :accId]?.Name;
| Allowed use-case | Example | More information |
|---|---|---|
| Method or variable or parameter chains | aObject?.aMethod(); | Can be used as a top-level statement. |
| Using parentheses, for example in a cast. | ((T)a1?.b1)?.c1() | The operator skips the method chain up to the first closing
parenthesis. By adding the operator after the parenthesis, the code
safeguards the whole expression. If the operator is used elsewhere,
and not after the parenthesis, the whole cast expression isn’t be
safeguarded. For example, the behavior of
|
| SObject chaining | String s = contact.Account?.BillingCity; | An SObject expression evaluates to null when the relationship is null. The behavior is equivalent to String s = contact.Account.BillingCity. |
| SOQL Queries | String s = [SELECT LastName FROM Contact]?.LastName; |
If the SOQL query returns no objects, then the expression
evaluates to null. The behavior is equivalent to:
|
You can’t use the Safe Navigation Operator in certain cases. Attempting to use the
operator in these ways causes an error during compilation:
- Types and static expressions with dots. For example:
- Namespaces
- {Namespace}.{Class}
- Trigger.new
- Flow.interview.{flowName}
- {Type}.class
- Static variable access, method calls, and expressions. For example:
- AClass.AStaticMethodCall()
- AClass.AStaticVariable
- String.format('{0}', 'hello world')
- Page.{pageName}
- Assignable expressions. For example:
- foo?.bar = 42;
- ++foo?.bar;
- SOQL bind expressions. For
example:
1class X { public String query = 'xyz';} 2X x = new X(); 3List<Account> accounts = [SELECT Name FROM Account WHERE Name = :X?.query] 4List<List<SObject>> moreAccounts = [FIND :X?.query IN ALL FIELDS 5 RETURNING Account(Name)]; - With addError() on SObject scalar
fields. For example:
1Contact c; 2c.LastName?.addError('The field must have a value');