13

In other languages (see, for instance, Swift), stating explicitly that a variable is final lets the compiler optimize the code accordingly.

Is this the case with Apex?

1

4 Answers 4

13

Too long for a comment, not a proper evaluation for 100% confidence

So if you run the following script in anonymous Apex you will find that the loop with the final variable always runs faster than the one with the non-final variable.

final Integer finalVal = 1000000;
Integer changeableVal = 1000000;
DateTime startTime;   

Long changeableDuration;
startTime = DateTime.now();
for(Integer i = 1; i <= changeableVal; i++) {
    if(i == changeableVal) {
        changeableDuration = DateTime.now().getTime() - startTime.getTime();
    }
}

System.debug('Changeable: ' + changeableDuration);

Long finalDuration;
startTime = DateTime.now();
for(Integer j = 1; j <= finalVal; j++) {
    if(j == finalVal) {
        finalDuration = DateTime.now().getTime() - startTime.getTime();
    }
}

System.debug('Final: ' + finalDuration);

Output Example:

13:52:42:612 USER_DEBUG [13]|DEBUG|Changeable: 610
13:52:43:172 USER_DEBUG [23]|DEBUG|Final: 559

It does not seem to matter the order that you code is run or the order the variables are declared, it just seems to run faster when declared final. This has only been tested on two orgs.

13

I only use it as part of making the intent clearer in code.

So to me:

private static final String OPEN = 'Open';

or:

private static final Set<DisplayType> LIKE_SUPPORTED = new Set<DisplayType>{
    DisplayType.STRING,
    DisplayType.PICKLIST,
    DisplayType.EMAIL,
    DisplayType.PHONE
};

are fairly clearly constants not expected to change.

(Though as Apex doesn't have immutable collections exposed you are not protected from accidentally adding or removing collection values.)

As to exactly what code is generated by having final present, the odds are that it will not have any impact in most code. zgc7009's measurement works out to a performance difference 0.06 microseconds per reference so hundreds or thousands of references will have an insignificant performance impact.

The second example though does prompt me to mention that as static variables are not preserved across transactions, initializing collections when a class is loaded can be costly, especially if there are many collections with many values and the collection values are only used in some methods. In that case it can be better to lazy load them:

// Only referenced in some methods so often not used in a transaction
private static final Set<DisplayType> LIKE_SUPPORTED {
    get {
        if (LIKE_SUPPORTED == null) {
            LIKE_SUPPORTED = new Set<DisplayType>{
                    DisplayType.STRING,
                    DisplayType.PICKLIST,
                    DisplayType.EMAIL,
                    DisplayType.PHONE,
                    ...
                    };
        }
        return LIKE_SUPPORTED;
    }
    private set;
};
4
  • 1
    Good point about lazy loading; I once shaved five minutes off of our deployment time by changing a class that was a list of final constants to lazy-loading values (there were some odd 400 values in this class, and it added 1 second to every transaction, and we had some odd 300 unit tests).
    – sfdcfox
    May 22, 2018 at 17:38
  • @sfdcfox, in this case is the getter to get the set values only called when someone tries to access the variable eventhough its declared as final? is that what you mean by lazy loading?
    – RedDevil
    May 22, 2018 at 18:31
  • @RedDevil The first time a method references LIKE_SUPPORTED it will be null and the collection will be created. Subsequent times it won't be null and the already created values will be returned. See e.g. Lazy loading.
    – Keith C
    May 22, 2018 at 18:33
  • @KeithC, Thank you ! just trying to understand it right. Small things but big performance gains :)
    – RedDevil
    May 22, 2018 at 18:34
7

One big optimization regardless of what the compiler does with the declaration is that you can eliminate many null checks from your code. Consider the below example. If you remove the final declaration, your code becomes susceptible to NullPointerException unless you add a null check.

public class Foo
{
    final List<String> collection;
    public Foo()
    {
        collection = new List<String>();
    }
    public void doStuff()
    {
        if (!collection.isEmpty()) { /*some implementation*/ }
    }
}

Checking collections != null && !collection.isEmpty() won't add a ton of processing time in any individual call, or even every thousand calls. But using final here does provide a tangible, measurable benefit, however small.

9
  • From a quick check, final does not protect from an NPE (which happens if the line collection = new List<String>(); is commented out). And perhaps some cost is added to the assignment as a second assignment generates a System.FinalException implying some extra checking code has been inserted.
    – Keith C
    May 22, 2018 at 16:54
  • It gives you knowledge about which variables can or cannot be null. It doesn't protect against NPE in and of itself.
    – Adrian Larson
    May 22, 2018 at 17:07
  • I'm not getting your point. AFAIK it only gives you knowledge of which variables generate a runtime error when an attempt is made to set a value a second and subsequent times.
    – Keith C
    May 22, 2018 at 17:16
  • If you know you set a final attribute in your constructor, you know the variable will never be null...
    – Adrian Larson
    May 22, 2018 at 17:18
  • OK I get your point now that the variable cannot be set back to null later in the code. Maybe final List<String> collection = new List<String>(); is a bit clearer an example than having to do something in a constructor.
    – Keith C
    May 22, 2018 at 17:21
7

There's a benefit for you, the developer, since you're restricting the possible ways a variable can be assigned. For example, you're more likely to catch accidental assignments (using = instead of ==), and by restricting when a variable can be initialized, you're more likely to remember to initialize it correctly (as Adrian said, reducing the need for null checks). However, the Apex Runtime itself won't gain any tangible benefit from using this keyword.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .