Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Book Overview & Buying Mastering Apex Programming
  • Table Of Contents Toc
  • Feedback & Rating feedback
Mastering Apex Programming

Mastering Apex Programming - Second Edition

By : Paul Battisson
4 (1)
close
close
Mastering Apex Programming

Mastering Apex Programming

4 (1)
By: Paul Battisson

Overview of this book

Applications built on the Salesforce platform are now a key part of many organizations' IT systems, with more complex and integrated solutions being delivered every day. As a Salesforce developer working with Apex, it is important to understand the range and variety of tools at your disposal, how and when to use them, and what the best practices are. This revised second edition includes a complete restructuring and five new chapters filled with detailed content on the latest Salesforce innovations including integrating with DataWeave in Apex, and utilizing Flow and Apex together to build scalable applications with Administrators. This Salesforce book starts with a discussion around common mistakes, debugging, exception handling, and testing. The second section focuses on the different asynchronous Apex programming options to help you build more scalable applications, before the third section focuses on integrations, including working with platform events and developing custom Apex REST web services. Finally, the book finishes with a section dedicated to profiling and improving the performance of your Apex including architecture. With code examples used to facilitate discussion throughout, by the end of the book you will be able to develop robust and scalable applications in Apex with confidence.
Table of Contents (28 chapters)
close
close
1
Section 1: Triggers, Testing, and Security
8
Section 2: Asynchronous Apex
13
Section 3: Integrations
21
Section 4: Apex Performance

Null pointer exceptions

Almost every developer working with the Salesforce platform will have encountered the dreaded phrase Attempt to de-reference a null object. At its heart, this is one of the simplest errors to both generate and handle effectively, but its error message can cause great confusion for new and experienced developers alike, as it is often unclear how the exception is occurring.

Let’s start by discussing in the abstract form how the error is generated. This is a runtime error, caused by the system attempting to read data from memory where the memory is blank. Apex is built on top of the Java language and uses the Java Virtual Machine (JVM) runtime under the hood. What follows is a highly simplified discussion of how Java manages memory, which will help us to understand what is happening behind the scenes.

Whenever an object is instantiated in Java, it is created and managed on the heap, which is a block of memory used to dynamically hold data for objects and classes at runtime. A separate set of memory, called the stack, stores references to these objects and instances. So, in simplistic terms, when you instantiate an instance of a Person class called paul, that instance is stored on the heap and a reference to this heap memory is stored on the stack, with the label paul. Apex is built on Java and compiles down to Java bytecode (this started after an update from Salesforce in 2012), and, although Apex does not utilize a full version of the JVM, it uses the JVM as the basis for its operations, including garbage and memory management.

With this in mind, we are now better able to understand how the two most common types of NullPointerException instances within Apex occur: when working with specific object instances and when referencing values within maps.

Exceptions on object instances

Let’s imagine I have the following code within my environment:

public class Person {
    public String name;
}
Person paul;

In this code, we have a Person class defined, with a single publicly accessible member variable. We have then declared a variable, paul, using this new data type. In memory, Salesforce now has a label on the stack called paul that is not pointing to any address on the heap, as paul currently has the value of null.

If we now attempt to run System.debug(paul.name);, we will get an exception of type NullPointerException with the message Attempt to de-reference a null object. What is happening is that the system is trying to use the paul variable to retrieve the object instance and then access the name property of that instance. Because the instance is null, the reference to this memory does not exist, and so a NullPointerException is thrown; that is, we have nothing to point with.

With this understanding of how memory management is working under the hood (in an approximate fashion) and how we are generating these errors, it is therefore easy to see how we code against them—avoid calling methods and accessing variables and properties on an object that has not been instantiated. This can be done by ensuring we always call a constructor when initializing a variable, as shown in the following code snippet:

Person paul = new Person();

In general, when developing, we should pay attention to any public methods or variables that return complex types or data from a complex type. A common practice is simply to instantiate new instances of the underlying object in the constructor for any data that may be returned before being populated.

Exceptions when working with maps

Another common way in which this exception presents itself is when working with collections and data retrieved from collections—most notably, maps. As an example, we may have some data in a map for us to use in processing unrelated records. Let’s say we have a Map<String, Contact> contactsByBadgeId instance that allows us to use an individual’s unique badge ID string to retrieve their contact record for processing. Let’s try to run the following:

String badBadgeId = 'THIS ID DOES NOT EXIST';
String ownerName = contactsByBadgeId.get(badBadgeId).FirstName;

Assuming that the map will not have the key value that badBadgeId is holding, the get method on the map will return null, and our attempt to access the FirstName property will be met with NullPointerException being thrown.

The simplest and most effective way to manage this is to wrap our method in a simple if block, as follows:

String badBadgeId = 'THIS ID DOES NOT EXIST';
if(contactsByBadgeId.containsKey(badBadgeId)) {
    String ownerName = contactsByBadgeId.get(badBadgeId)
  .FirstName;
}

By adding this guard clause, we have proactively filtered out any bad keys for the map by removing the error.

As an alternative, if we were looping through a list of badge IDs, like this:

for(String badgeId : badgeIdList) {
    String ownerName = contactsByBadgeId.get(badBadgeId).FirstName;
}

We could also use the methods available on set collections to both potentially reduce our loop size and avoid the issue, as follows:

Set<String> badgeIdSet = new Set<String>(badgeIdList).retainAll(contactsByBadgeId.keySet());
for(String badgeId : badgeIdSet) {
    String ownerName = contactsByBadgeId.get(badBadgeId).FirstName;
}

In the preceding example, we have filtered down the items to be iterated through to only those in the keySet instance of the map. This may not be possible in many instances, as we may be looping through a collection of a non-primitive type or a type that does not match our keySet instance. In these cases, our if statement is one solution. We can also use a feature called safe navigation to allow Apex to assist us in avoiding these issues.

Safe navigation operator

The discussion and methods shown in the preceding sections provide ways for us to code to avoid a NullPointerException instance occurring through the way we structure our code, which can also assist in performance. For example, the second set of code for looping through a pre-filtered list of IDs based on the map’s keys will avoid unnecessary looping and operations. Since the first edition of this book was published, Salesforce has added in a safe navigation operator that helps remove a lot of the null checks we previously had to perform.

The safe navigation operator (?.) will return null if a value is unavailable (when previously a NullPointerException instance would occur) and return a value if one is available. We can rewrite our badge ID retrieval code as follows:

String badgeId = 'THIS ID DOES NOT EXIST';
String ownerName = contactsByBadgeId.get(badBadgeId)?.FirstName;

In this instance, if badgeId is a key on the map and can be retrieved, then the FirstName value for the contact is assigned to ownerName. If the value of badgeId is not a valid key, then ownerName is set to null. We can see some more examples in the following code block:

Integer employeeCount = myAccount?.NumberOfEmployees; //returns null if myAccount is not an Account instance
paul?.getContactRecord()?.FirstName; //returns null if paul is null or
  the getContactRecord() method returns null
String accName = [SELECT Name FROM Account WHERE Account_Reference__c 
  = :externalAccountKey]?.Name; //returns null if no Account record is
    found by the query

The safe navigation operator is an extremely useful operator to help developers in minimizing errors; however, it should not be considered a panacea. You may unintentionally cause further NullPointerException instances within your code by not correctly verifying whether a null value has been returned. In the following example, we take our badge ID code to retrieve the name of the badge’s owner:

String ownerName = contactsByBadgeId.get(badBadgeId)?.FirstName;

We may then use this value within a component or page to display a greeting to the individual. If a null value has been returned, this can lead to some unexpected messages:

String ownerName = contactsByBadgeId.get(badBadgeId)?.FirstName;
String welcomeMessage = 'Hello there ' + ownerName;
//"Hello there null" would be displayed as a greeting.

It is important, therefore, that within your code base and among your development team an agreement is reached as to where the responsibility for these checks lies within the code to ensure that unintended consequences do not occur.

In general, most NullPointerException instances occur when a premature assumption about the availability of data has been made—for example, that the object has been instantiated or that our map contains the key we are looking for. Trying to recognize these assumptions will assist in avoiding these exceptions, going forward. We can also use the safe navigation operator in instances where we want to ensure safety to allow the code execution to continue, but must then be aware of checking for null values within our code. With this in mind, let us now look at how we can effectively bulkify our Apex code.

Visually different images
CONTINUE READING
83
Tech Concepts
36
Programming languages
73
Tech Tools
Icon Unlimited access to the largest independent learning library in tech of over 8,000 expert-authored tech books and videos.
Icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Icon 50+ new titles added per month and exclusive early access to books as they are being written.
Mastering Apex Programming
notes
bookmark Notes and Bookmarks search Search in title playlist Add to playlist font-size Font size

Change the font size

margin-width Margin width

Change margin width

day-mode Day/Sepia/Night Modes

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Confirmation

Modal Close icon
claim successful

Buy this book with your credits?

Modal Close icon
Are you sure you want to buy this book with one of your credits?
Close
YES, BUY

Submit Your Feedback

Modal Close icon
Modal Close icon
Modal Close icon