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?
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It only takes a minute to sign up.
Sign up to join this communityIn 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?
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.
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;
};
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.
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.
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.
final
attribute in your constructor, you know the variable will never be null...
final List<String> collection = new List<String>();
is a bit clearer an example than having to do something in a constructor.
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.