Static in Apex Salesforce
Hello devs,
Static variables are a key aspect of programming that can significantly enhance code functionality and efficiency.
In this post, I’ll provide a brief introduction to static variables, shedding light on their importance in apex development.
Introduction
Let’s start with definitions:
The
static
keyword is a non-access modifier used for methods and attributes. Static methods/attributes can be accessed without creating an object of a class.The
static
keyword is used to associate a variable or method with a class rather than a specific instance of the class.
But what does it all mean?
Talk is cheap, show me the code!
Static variable/method
Variable/method associated with a class
public class Calculator {
public static Double sumOfAll = 0;
public static Double add(Double a) {
sumOfAll += a;
return sumOfAll;
}
}
System.debug(Calculator.sumOfAll); // 0.0
Calculator myCalculator = new Calculator();
System.debug(myCalculator.sumOfAll);
// error: Static field cannot be referenced from a non static context: sumOfAll from the type myCalculator
Calculator.add(2);
System.debug(Calculator.sumOfAll); // 2.0
Calculator.add(4);
System.debug(Calculator.sumOfAll); // 6.0
Calculator.add(8);
System.debug(Calculator.sumOfAll); // 14.0
Instance variable/method
Variable/method associated with a specific instance of the class
public with sharing class Calculator {
private Double a;
private Double b;
public Calculator(Double a, Double b) {
this.a = a;
this.b = b;
}
public Double add() {
return a + b;
}
public Double subtract() {
return a - b;
}
}
System.debug(Calculator.a); // error: Variable does not exist: a
System.debug(Calculator.b); // error: Variable does not exist: b
Calculator calculator1 = new Calculator(128, 64);
System.debug(calculator1.add()); // 192.0
System.debug(calculator1.subtract()); // 64.0
Calculator calculator2 = new Calculator(256, 8);
System.debug(calculator2.add()); // 264.0
System.debug(calculator2.subtract()); // 248.0
Consideration
- The
static
keyword can be only used in the outer class and cannot be a part of the inner class.
You can use static methods and variables only with outer classes. Inner classes have no static methods or variables. A static method or variable doesn’t require an instance of the class in order to run. ~ Salesforce
public class MyOuterClass {
public static Boolean isActive = true; // valid
public class MyInnerClass {
public static Boolean isActive = true; // error: static can only be used on fields of a top level type
}
}
Static
variables are created only when a class is loaded or used. Variables associated with a specific instance of the class are created with every object instantiated from the class.Static
variables are reset in Unit Tests. There is Salesforce Idea to change this behavior.Static
does NOT require an instance of the class.
As was mentioned in the introduction:
static
keyword makes a variable or method associated with a class rather than a specific instance of the class.
public class MyClass {
public static Boolean isActive = true;
}
System.debug(MyClass.isActive); // valid
MyClass myClass = new MyClass();
System.debug(MyClass.isActive); // error: Static field cannot be referenced from a non static context: isActive from the type MyClass
Static
is only in the scope of the transaction and will not persist longer than that transaction.Static
values cannot be shared between multiple execution contexts. To save state between transaction, values need to be saved to the database e.g., as an object record, platform cache, or custom settings).
A static variable is static only within the scope of the Apex transaction. It’s not static across the server or the entire organization. The value of a static variable persists within the context of a single transaction and is reset across transaction boundaries. ~ Salesforce
Static
lifetime can be consider as the main benefit of static
. It allows you to create a simple singleton. A variable will be accessible throughout the transaction. More details in the use case section.
Its behavior can be modified using getters.
A simple cache approach:
public class CurrentUserInfo {
public static User userDetails = [SELECT Id, Name, Email FROM User WHERE Id = :UserInfo.getUserId()];
}
User currentUser = null;
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(1, Limits.getQueries(), 'Only 1 SOQL query was consumed');
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(1, Limits.getQueries(), 'Only 1 SOQL query was consumed');
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(1, Limits.getQueries(), 'Only 1 SOQL query was consumed');
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(1, Limits.getQueries(), 'Only 1 SOQL query was consumed');
public class CurrentUserInfo {
public static User userDetails {
get {
return [SELECT Id, Name, Email FROM User WHERE Id = :UserInfo.getUserId()];
}
}
}
User currentUser = null;
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(1, Limits.getQueries(), '1 SOQL query was consumed');
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(2, Limits.getQueries(), '2 SOQLs query were consumed');
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(3, Limits.getQueries(), '3 SOQLs query were consumed');
currentUser = CurrentUserInfo.userDetails;
Assert.areEquals(4, Limits.getQueries(), '4 SOQLs query were consumed');
Static
cannot be accessed through an instance of the class.
public class MyClass {
public static Boolean isActive = false;
}
System.debug(MyClass.isActive); // false
MyClass example1 = new MyClass();
System.debug(example1.isActive); // error: Variable does not exist: isActive
Static
methods cannot usethis
keyword becausestatic
is NOT associated with a specific instance of a class.
public class MyClass {
public Boolean isActive = false;
public static Boolean getIsActive() {
return this.isActive; // error: This cannot be referenced in a static context
}
}
Static vs Non-static
Scenario | Static | Non-Static (Instance) |
---|---|---|
Ownership | Belongs to the class | Belongs to the instance (new ) of the class |
Usage | Can ONLY be invoked from the class MyClass.staticVariable; |
Can ONLY be called form an instance of the class new MyClass().nonStaticVariable; |
Combined with | ONLY static methods; this cannot be used |
Static and non-static can be used; this can be used |
Memory | Initialized only when a class is loaded | Created with every object instantiated (new is called) from the class in which they’re declared |
Best practices
While static variables can be useful for sharing data, it’s generally recommended to avoid excessive reliance on global state. Global states can make code harder to understand, maintain, and test.
Use Cases
Utils
A
static
method is used as a utility method, and it never depends on the value of an instance member variable. ~ Salesforce
RecordTypesInfo.cls
As you can see in the code below, RecordType information will be stored in static
variables, making it accessible across all Apex classes within the entire transaction.
public with sharing class RecordTypesInfo {
private static Map<sObjectType, Map<String, Schema.RecordTypeInfo>> recordTypesInfo = new Map<sObjectType, Map<String, Schema.RecordTypeInfo>>();
public static Schema.RecordTypeInfo get(SObjectType objectType, String recordTypeDeveloperName) {
Map<String, Schema.RecordTypeInfo> recordTypesInfoForSObject = recordTypesInfo?.get(objectType);
if (recordTypesInfoForSObject == null) {
recordTypesInfoForSObject = objectType.getDescribe(SObjectDescribeOptions.DEFERRED).getRecordTypeInfosByDeveloperName();
recordTypesInfo.put(objectType, recordTypesInfoForSObject);
}
return recordTypesInfoForSObject?.get(recordTypeDeveloperName);
}
}
Id partnerAccountRecordTypeId = RecordTypesInfo.get(Account.SObjectType, 'Partner');
Id customerAccountRecordTypeId = RecordTypesInfo.get(Account.SObjectType, 'Customer');
Consts
Apex Consts
It is a common approach to keep constant variables static
. As mentioned above, "static variables are initialized once," which allows for saving Apex heap size.
Singleton
To store information that is shared across instances of a class, use a static variable. All instances of the same class share a single copy of the static variable. ~ Salesforce
Let’s take a class from Constants in Apex.
public class ContactConsts {
public static final ContactConsts INSTANCE = new ContactConsts();
private ContactConsts() {}
public final String API_NAME = Contact.sObjectType.getDescribe().getName();
public final LeadSource LEAD_SOURCE = new LeadSource();
private class LeadSource {
public final String OTHER = 'Other';
public final String PARTNER_REFERRAL = 'Partner Referral';
public final String PHONE_INQUIRY = 'Phone Inquiry';
public final String PURCHASED_LIST = 'Purchased List';
public final String WEB = 'Web';
}
}
// All variables will be loaded once
System.debug(ContactConsts.INSTANCE.API_NAME);
Recursive Triggers
For example, all triggers that a single transaction spawns can communicate with each other by viewing and updating static variables in a related class. A recursive trigger can use the value of a class variable to determine when to exit the recursion ~ Salesforce
A lot of trigger frameworks use static
variables to store information about:
- disabled triggers
- disable trigger handlers
- or trigger recursion
Summary
Static variables are associated with the class rather than a specific instance. They are created when a class is loaded and persist throughout the transaction. Static variables are useful for maintaining state, creating utility methods, holding constant values, and implementing the singleton pattern. However, excessive use of static variables can lead to code complexity and difficulty in testing and maintenance. It is important to use static variables judiciously and consider the overall design and architecture of your code.
I hope this provides a comprehensive overview of static variables in Apex development. If you have any further questions, feel free to ask!