Piotr Gajek
written by Piotr Gajek
posted on July 2, 2023
Technical Architect and Full-stack Salesforce Developer. He started his adventure with Salesforce in 2017. Clean code lover and thoughtful solutions enthusiast.
Table of Content

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 use this keyword because static 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.

Constants in Apex

Consts framework (github)

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();

    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!

Resources