This little and simple post is to get Community’s attention to a possible bug my colleagues and me found few days ago.
It’s about how null checking differs if occurring from outside or inside an Apex Class.
For those who are TL;DR (like me), jump to the following Gist and comment if you have an explanation.
Let’s take this simple class:
public class MyController { public Account tst{get;set;} public MyController(){ this.tst = [Select Id, Name From Account limit 1]; } public void myMethod(){ system.debug('Test inside: '+(tst.Name == null)); //Throws null pointer exceptions system.debug('Test inside: '+(tst == null)); system.debug('Test inside: '+json.serializepretty(tst)); } }
Let’s take the following anonymous code:
MyController cnt = new MyController(); cnt.tst.Name = 'test'; cnt.tst = null; system.debug('Test outside: '+(cnt.tst.Name == null)); system.debug('Test outside: '+(cnt.tst == null)); system.debug('Test outside: '+json.serializepretty(cnt.tst)); cnt.myMethod();
Question: What do you think is the debug?
Answer:
19:06:58.24 (32903967)|USER_DEBUG|[6]|DEBUG|Test outside: true
19:06:58.24 (33182107)|USER_DEBUG|[7]|DEBUG|Test outside: null
19:06:58.24 (33393615)|EXCEPTION_THROWN|[7]|System.NullPointerException: Attempt to de-reference a null object
Yes, that is!
Apparently calling a nullified member of type Sobject of an object instance from outside is somehow handled by the runtime as null, whether you recall a field (cnt.tst.Name == null) or the member itself (cnt.tst == null); while calling the same member from inside an object method, correctly throws a null pointer exception as expected.
This seems related to how Sobjects are handled when used in SOQL relationship fields, such as:
List<Contact> cntList = [Select Id, Account.Name From Contact limit 1]; System.debug('Account is: '+cntList[0].Account); System.debug('Account.Name is: '+cntList[0].Account.Name);
In this example if the Account parent object is not presente, the debug outputs:
19:18:17.18 (79634328)|USER_DEBUG|[4]|DEBUG|Account.Name is: null
This is somehow confusing.
I get that when dealing with SOQL queries this is useful (no null checking) but dealing with custom Apex Classes this can lead to real confusion.
These are my 2 cents, what do you think?
Leave a Reply