Grokking – Apex

This is a cross post from my blog here.

Over the past few years, as a developer, I have come across a few interesting features and gotchas on the platform that are either not documented, or is documented but developers don’t know about it. This post describes some of the quirks and some unknown facts of the Apex language that you might or might not have seen. Let’s dive in!

Map accepts null as a key

According to the Apex docs:

A map is a collection of key-value pairs where each unique key maps to a single value. Keys can be any primitive data type, while values can be primitive, sObject, collection type or an Apex object.

And right at the bottom of the page, it also notes:

A map key can hold the null value

So the following code is a perfectly legitimate apex code:

Map<Id, String> testMap = new Map<Id, String>();
testMap.put(null, 'foobar');
System.debug('the value of the null key is: '+testMap.get(null));
//outputs: The value of the null key is: foobar

Allowing null as a key to a map is a design flaw IMHO, therefore, it is a good idea to check for null before adding the key to a map variable. This information is also useful if you are wondering, why your map is returning null values?!?, when it’s not supposed to, especially, when you instatiate a map with a SOQL result where the values returned can be null unless you explicity check for null values in the where clause.

Unit Tests increment your Objects unique Id

This is not covered in the documentation, or atleast I haven’t found it in the Apex docs. When you run unit tests that insert/create records for a particular object(standard or otherwise), it increments the value of the Name field(of type Auto Number) outside of the test context.

Let’s say you have a custom object “Invoice Statement” (Invoice_Statement__c) that has a Name field called “Invoice Number”, and is of type “Auto Number”. The format of the field is: INV-{0000}, and let’s assume that the latest record number is INV-2058.

If you insert 10 records in the test class as follows:

// Let's assume that this runs inside of a test method
List<Invoice_Statement__c> invStatementList = new List<Invoice_Statement__c>();
for(Integer i = 0; i < 10; i++) {
  invStatementList.add(new Invoice_Statement__c(company='acme ltd');
insert invStatementList;

Now, when you insert a new Invoice Statement record outside of the test method via a trigger, or batch, or the User Interface, the next inserted record will have the id INV-2069. So don’t rely on the unique name field if you have strict rules around the auto-increment feature in your application. Instead add a custom field which is auto-incremented in a workflow or trigger to have more granular control over how the value is incremented.

Divide (/) operator gotcha

What do you think should be the output of this statment?

 system.debug('7 divided by 2 is: '+7/2);

If you think it should be 7 divided by 2 is: 3.5, then you are wrong. The actual output is – 7 divided by 2 is: 3. If the operands of the divide(/) operator are both integers, the result is an integer.

If you want a decimal output then one of the operands must be a decimal, so the following code:

 system.debug('7 divided by 2 is: '+7.0/2);

will output 3.5. I have spent many hours trying to figure out what’s wrong with my code when I realized that x/y (x and y being integers) wasn’t returning an accurate decimal value. So make sure when you use the divide operator atleast one of the operands is a decimal.

Improve SOQL and SOSL performance by adding NULLability check

This is actually highlighted in the Apex docs, but I have seen many experienced devs(including myself) not following this simple, but very effective suggestion to improve SOQL and SOSL query performance.

You can improve the performance of your code by filtering out null values in your SOQL and SOSL queries.

List<String> invNumList = {'001', '002', '003'};
List<Invoice_Statement__c> invStatList = [Select Id, invNumber from Invoice_Statement__c
                                          where invNumber in : invNumList
                                          and invNumber != null];

That extra check for nullability improves the performance because it avoids searching records that contain null values.

I hope these tips are useful to some of you! I plan to write more of these in the future. Let me know if you’ve come across other interesting Apex code gotchas in the comments.

Tagged , ,

10 thoughts on “Grokking – Apex

  1. Rich Unger says:

    Just FYI, java HashMap allows null keys as well, and integer division works the same way in most strongly typed languages, including java.

    • anupj says:

      Hi Rich,

      Yeah, I know that Java HashMap allows null key, which is why my java code was littered with null checks. 🙂

      Integer division behaviour is okay but a bit quirky, and might trip up new developers, which is why I’ve included it in my post.

      – Anup

  2. LaurentD says:

    Good post

  3. raygao says:

    Another one I found about Visualforce -> you can NOT nest inside the loop. The workaround is to reverse wrap outside of and use a javascript to update inner tags.

  4. raygao says:

    I meant nesting apex:form with a specific id inside the apex:repeat does not work. But, you can do it other way around. 🙂 This is a limitation of Visualforce.

    • anupj says:

      Thanks for pointing that out Ray! I’m actually working on a follow up post on Visualforce gotchas soon! Will include this in that post. 🙂

  5. Chris says:

    About the point divide, this trick does work for real integer but not for variable of type integer. the solution is to convert it directly as a decimal or double:
    system.assertEquals(3.5, decimal.valueOf(7) / decimal.valueOf(2)) ;

    About the UnitTest problem … wow … I never realized that before, and never thought this could happen – I so less thought of such a “bug” that I verified that by myself and this happens indeed. pretty weird, and thanks anyway for the info!

    • anupj says:

      I am glad you liked the post, Chris. You are absolutely right about the integer variables. My point was more to illustrate how an integer division with literals returns a truncated integer instead of a decimal. Thanks for pointing it out though!

      • Chris says:

        Actually there is a way to make this auto number sequential by activating following feature done by the support ‘Enable separate autonumbers for Apex tests’ – although they still advise, I quote : “I informed you that our autonumber functionality does not guarantee sequence, only order. A different solution should be used if this is required.” . So they don’t take any guarantee on this feature although it seems to work.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: