function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
ArmouryArmoury 

Null Pointer Exception

Hi.. I just came noticed an unknown behavior (to me) in Apex.
I used the below query to pull the data from Case Object.
Select Id, Account.PersonMailingPostalCode from Case
And the below are my System.debug statements..
System.debug('********** Account = ' + caseList[0].Account);
System.debug('********** PersonMailingPostalCode = ' + caseList[0].Account.PersonMailingPostalCode);
The debug logs shows as below without any exception..
'********** Account = null
*********** PersonMailingPostalCode = null
which means that the account is not associated to the case yet. So the caseList[0].Account is null
So how come the second statement to fetch the postal code from Account doesnt throw Null pointer exception but still prints as null. Is this because of the way I query the record??
 
Best Answer chosen by Armoury
ArmouryArmoury
Derhyk - The accountId returns null as well in the debug statment.
Temoc - I just found out this in the apex documentation.
Note: The expression c.Account.Name, as well as any other expression that traverses a relationship, displays slightly different characteristics when it is read as a value than when it is modified:
• When being read as a value, if c.Account is null, then c.Account.Name evaluates to null, but does not yield a
NullPointerException. This design allows developers to navigate multiple relationships without the tedium of having
to check for null values.
• When being modified, if c.Account is null, then c.Account.Name does yield a NullPointerException.

I think the above statment is true not only for traversing the relationships but also for all the objects which SOQL returns the result.
I tried the below code.
 
List<Case> caseList = [Select Id, Account.PersonMailingPostalCode from Case];
System.debug('******* Instance of Resut = ' + caseList[0] instanceof Case);

// received the compilation error message for the above code as - Incompatible types since an instance of String is never an instance of Case
But I do get the exception when i assigned the account to the object like below
 
List<Case> caseList = [Select Id, Account.PersonMailingPostalCode from Case];
Account dummyAccount = caseList[0].Account;
System.debug('Result  = ' + accountDummy.PersonMailingPostalCode);

// The result is Null pointer exception in line 3.


 

All Answers

Derhyk Doggett -Derhyk Doggett -
If you changed the first debug line to capture AccountId, does it return a value?
System.debug('********** Account = ' + caseList[0].AccountId);
-derhyk
Temoc MunozTemoc Munoz
Based on the Salesforce.com library for Java, we can get an idea on how Salesforce does this under the hood:
https://developer.salesforce.com/page/Introduction_to_the_Force.com_Web_Services_Connector

What I have below is just an idea of what I think is going on and should not be taken as the real Salesforce implementation:
 // Under the Case object (Real Salesforce library)
 CustomField cs = new CustomField();
 cs.setFullName('Account');
 cs.setLabel("CustExtField");
 cs.setType(FieldType.Text);
 cs.setExternalId(true);
.............
SaveResult[] results = metadataConnection.updateMetadata(new Metadata[] {cs});

// cs now has an Account field (possibly a custom class in Java with a field called value or a Map of fields for Cases) i.e. 
// For explanation purposes only
public class Case__
{
    Account__c Account = new Account();
   
    public Case()
    {
        Account.setAccountValue(null);
    }
}

 // Under the Account object (Real Salesforce library)
 CustomField cs = new CustomField();
 cs.setFullName('PersonMailingPostalCode');
 cs.setLabel("CustExtField");
 cs.setType(FieldType.Text);
 cs.setExternalId(true);
...............
SaveResult[] results = metadataConnection.updateMetadata(new Metadata[] {cs});


// For explanation purposes only
public class Account__
{   
     String PersonMailingPostalCode= ''; 
  
     public Account__() 
     {   
     } 
     
     public void setAccountValue(){...}
     public void setPersonMailingPostalCodeValue(){...}
}


 
ArmouryArmoury
Derhyk - The accountId returns null as well in the debug statment.
Temoc - I just found out this in the apex documentation.
Note: The expression c.Account.Name, as well as any other expression that traverses a relationship, displays slightly different characteristics when it is read as a value than when it is modified:
• When being read as a value, if c.Account is null, then c.Account.Name evaluates to null, but does not yield a
NullPointerException. This design allows developers to navigate multiple relationships without the tedium of having
to check for null values.
• When being modified, if c.Account is null, then c.Account.Name does yield a NullPointerException.

I think the above statment is true not only for traversing the relationships but also for all the objects which SOQL returns the result.
I tried the below code.
 
List<Case> caseList = [Select Id, Account.PersonMailingPostalCode from Case];
System.debug('******* Instance of Resut = ' + caseList[0] instanceof Case);

// received the compilation error message for the above code as - Incompatible types since an instance of String is never an instance of Case
But I do get the exception when i assigned the account to the object like below
 
List<Case> caseList = [Select Id, Account.PersonMailingPostalCode from Case];
Account dummyAccount = caseList[0].Account;
System.debug('Result  = ' + accountDummy.PersonMailingPostalCode);

// The result is Null pointer exception in line 3.


 
This was selected as the best answer
Derhyk Doggett -Derhyk Doggett -
Armoury,
I should have mentioned in my first post. To get the additional fields (ie AccountId) you will need to add those to your query. Any fields on records not in the trigger will need to be queried for. 
You only get the current records direct fields for "free". Any other record fields on other similar object records or related (parent, child) records will need to be queried for. 

-derhyk 
ArmouryArmoury
Thanks for the note Derhyk. I am aware of that dependency where we have to query the fields in order to use it else a runtime exception happens. But there is a special case for relationship objects described in the apex developer guide as 
sObject records represent relationships to other records with two fields: an ID and an address that points to a representation of the associated sObject.
So in my below query I am referring to the Child object's field (Account's Name) apex automatically retrieves the ID as well and I am able to print the AccountId without actually including it in the SOQL.
List<Case> caseList = [Select Id, Account.Name from Case];
System.debug(caseList[0].Account.Name); // Works fine.
System.debug(caseList[0].AccountId); // Works fine. No Compilation/RunTime issue.
But yeah - the main reason this thread was started was to check why the Null pointer exception was not happening and as per my previous reply the apex document itself says that it is by design for read only values.
Temoc MunozTemoc Munoz
Good one Armoury!!