Declare variables, constants, methods, and use modifiers and Apex interfaces.

After studying this topic, you should be able to:

  • Describe the different variable data types such as primitive data types and non-primitive data types
  • Determine how to create methods and understand how access modifiers define method accessibility
  • Identify what interfaces are, how to define, and when to use them to meet a requirement

Table of Contents

  • Primitive Data Types
  • Non-Primitive Data Types
  • Variables and Expressions
  • Rules of Conversion
  • Declaring Methods
  • Modifiers
  • Interfaces
  • Implementation Examples
  • Interface Scenarios

Introduction

  • Primitive data types are the most basic data types provided by Salesforce, whereas non-primitives are used to handle more complex data types based on requirements
    • Non-Primitive data types such as collection require primitive data types and can be used to specify a list, set, or map of elements
    • Other data types like sObjects and enum can also be used
  • This topic also covers how to declare variables and constants and assign values
    • Apex provides various data types for variables, constants, expressions, and expression operators for initializing and accessing data
      • All variables and expressions have a data type in Apex
      • Collections can store multiple elements
  • Primitive data types:
    • Blob: collection of binary data
    • Boolean: true or false value
    • Date: particular date
    • Datetime: particular date and time
    • Decimal: currency values
    • Double: 64-bit number with a decimal point
    • Id: valid 18-digit identifier
    • Integer: 32-bit number without decimal point
    • Long: 64-bit number without decimal point
    • Object: any data type in Apex
    • String: a set of characters
    • Time: particular time of day
  • Non-primitive data types:
    • List: an ordered collection of elements
    • Set: unique collection of elements
    • Map: key-value pairs of elements
    • Enum: set of possible values
    • sObject: record of an object
    • Class Object: access class methods and variables

Primitive Data Types

  • Blob: String str = 'String'; Blob b = Blob.valueof(str);
    • Stores a collection of binary data as a single object. valueof method can be used to declare a Blob variable by casting a specified string.
    • Can be used as a web service argument, body of document, or an attachment
  • Boolean: Boolean isCorrect = false;
Boolean isCorrect = true;

if (isCorrect) {
    System.debug('The value of x is True');
}
else if (!isCorrect) {
    System.debug('The value of x is False');

}
else {
    System.debug('The value of x is Null');
}
  • Date: Date customDate = Date.newInstance(1939, 9, 1);
    • Declared using newInstance method
    • System static methods are used to create date values
  • DateTime: DateTime customDateTime = DateTime.newInstance(1963, 11, 22, 12, 30, 0);
    • System static methods are used to create date and time values
Datetime lastModifiedDate = [SELECT LastModifiedDate
                               FROM Lead
                              LIMIT 1].LastModifiedDate;
System.debug('Last Modified Date: ' + lastModifiedDate);
  • Decimal: Decimal a = 1.897;
    • Arbitrary precision number, use when the number of decimal places needs to be explicitly set
Decimal totalCost = 0;
totalCost += 726.234;
System.debug('totalCost' + totalCost);
  • Double: Double b = 1987971237098409233.0285
    • 64-bit number with a decimal
  • ID: ID id = '50041000005jHu4AAE'
    • Any valid 18-character Salesforce record identifier, if 15-character ID is provided, it will be converted to an 18-character
  • Integer: Integer num = 13;
    • Signed 32-bit number without a decimal
  • Long: Long l = 8240912804813839
    • 64-bit number without a decimal
  • Object: String s = (String) obj;
    • Any data type supported in Apex, as all Apex data types inherit from Object
    • Any Object variable that represents a more specific data type can be casted to the underlying data type
    • Can represent any primitive data type, sObjects, or user-defined classes
    • Use when a declared object needs to be cast to a specific data type
// cast Object to an Integer
Object obj1 = 12345;
Integer i = (Integer)obj1;
System.debug(i); // 12345

// cast Object to a String
Object obj2 = 'Salesforce';
String s = (String)obj2;
System.debug(s); // Salesforce

// cost Object to an Apex class
Object obj3 = new MyClass();
MyClass mc = (MyClass)obj3;
mc.printName(); // Martin Gessner
  • String: String variableName; String variableName = 'value';
  • Time: Time t = Time.newInstance(7, 30, 50, 500);
    • Declared using the newInstance method

Non-Primitive Data Types

  • Collection variables can be used to store multiple variables of the same data type:
    • List, Set, Map

List

  • List: ordered, indexed (zero-based) collection of primitives/non-primitives
  • List methods such as add(), get(), remove(), and set() can be used to manage elements in a list
  • Array notation such as myListItems[0] can be used
  • Use when several elements of a particular dat type need to be stored in a specific order
List<account> accounts = [SELECT Name FROM Account LIMIT 10];

Set

  • Set: unordered collection of unique elements that cannot contain duplicate values
  • Set methods such as add() and remove() can be used to manage elements in a set
  • Use when unique elements of a particular data type need to be stored in no specific order
Set<Id> accIds = new Set<Id>();
for (Contact con:[SELECT AccountID FROM Contact LIMIT 5]) {
    accIds.add(Con.AccountID);
}
System.debug('Set of Account IDs' + accIds); // Stores unique account IDs from a query result

Map

  • Can be declared using the Map keyword followed by the data types of the key and value within <> characters
  • Key-value pairs can be populated by specifying the key and then the value using the => operator within curly brace {} syntax
  • Can be populated directly from results returned by a SOQL query
    • Key: declare with an ID or String data type
    • Value: declare as an sObject data type
  • Use when data needs to be stored and accessed using identifiers or keys:
    • Ex: IDs and names of account records
Map<Integer,String> productMap = new Map<Integer, String>{8976 => 'Laptop E1500',
                                                          8977 => 'Keyboard R500'}
Map<Id, Opportunity> oppMap = new Map<Id,Opportunity>([SELECT Id, Name, StageName
                                                         FROM Opportunity])

Enum

  • Enum is a data type that specifies a set of constants
  • Use when a variable should only have one of a predefined set of values
    • Ex: specifying all the possible locations of a business in a country, out of which a specific location can be used in Apex
public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
public class Week {
    public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }

    public Day convertStringToEnum(String name) {
        return Day.valueOf(name);
    }
}
Week w = new Week();
Week.Day wd = w.convertStringToEnum('sunday');
System.debug(wd); // SUNDAY

sObject

  • Represents a row of data with fields
  • Use when need is to store a record of standard or custom objects
  • Specific sObject variable can be declared using the SOAP API name of the object and the new operator
  • Generic sObject variable can also be declared using the new operator
Account acc = new Account();
sObject obj = new Account();
sObject account1 = new Account();
sObject project1 = new Project__c();

System.debug('generic: ' + account1); // generic: Account:{}
System.debug('generic: ' + project1); // generic: Project__c:{}

Account account2 = new Account();
Project__c project2 = new Project__c();

System.debug('specific: ' + account2); // specific: Account:{}
System.debug('specific: ' + project2); // specific: Project__c:{}

Class Objects

  • An object created for a user-defined or system-supplied Apex class
  • Use when an object of a class needs to be instantiated in order to access non-static methods and variables of the class
  • An object of a class can be declared using the class name and the new operator
  • Class Object Declaration:
    • Constructor can be called to initialize field values
SalesOrder order = new SalesOrder('Laptop T1000', 1, 1259874);

Variables and Expressions

  • Apex is strongly-typed: data type must be declared before a variable can be used
  • Initial value: all variables are set to null if they are not assigned a value during initialization
  • Variable scope: two variables with the same name cannot exist in the same scope or “hierarchy” of blocks
    • Variables will be available in sub-blocks but not in parent blocks
  • A constant is a variable whose value cannot change after it has been assigned
    • static final Integer INT_CONST = 200;
  • A static variable declared in an Apex class can be directly accessed without instantiating using the syntax: ClassName.StaticVariableName
public class TestClass {
    public static Integer x = 777;
}
System.debug('Value of x: ' + TestClass.x);
  • transient keyword is used to declare variables that cannot be saved
    • Common usage is a field on a Visualforce page that is utilized only during the page request
public with sharing class CustomController {
    private final ID accountId;

    // Transient variables will not be transmitted as part of the view state
    transient public final String accountName { get; set; }
}

Rules of Conversion

  • Explicit conversion: Apex generally requires explicit conversion of one data type to another
    • Ex: String.format, Strong.valueOf or casting a variable to (String) before assigning it
  • Implicit conversion:
    • Lower (in terms of precision) numeric types can be assigned to higher types without explicitly converting the data type. Precision hierarchy:
      • Decimal » Double » Long » Integer
    • Other data types can be implicitly converted:
      • Variable of ID data type can be assigned to a String
      • Strings can be assigned to an ID, but ID validation is executed at runtime, so an exception may be thrown if not a valid ID
      • So, the instanceOf keyword can be used to test if a string is a valid ID

Declaring Methods

  • Apex methods consist of statements grouped together as a single unit in an Apex class that perform a specific business logic
    • Access modifiers: such as public or protected can be added to the method definition, but are optional
    • void: if a method does not return a value, use void instead of a return type
    • Input parameters: separate by commas, preceded with its data type, enclosed in ()
      • If no parameters, use empty parentheses ()
    • static keyword: used to define a static method which does not require an instance of a class to run
    • override keyworkd: used to override a method in a class defined as virtual or abstract
  • Required attributes: method name, data type, method parameters (or arguments), body enclosed in braces {}. Syntax below:
[public | private | protected | global] [override] [static] data_type
method_name (input parameters)  {
    // body of the method
}
  • Following example accepts string arguments, performs an update, returns a list of accounts
public static List<Account> updateRecords(String i, String t) {
    List<Account> accounts = [
        SELECT Id, Rating
          FROM Account
         WHERE Industry = :i AND Type :t
    ];

    for (Account a : accounts) {
        a.Rating = 'Hot';
    }

    update accounts;

    return accounts;
}

Modifiers

  • Access modifiers, like public or private, can be used when defining Apex methods or variables
    • Access modifier is required in the declaration of a top-level Apex class
    • Default: private, which means the method or variable can only be accessed within the Apex class
    • public: means that the method or variable can be used by any Apex in the application or namespace
    • global: means the method or variable can be used by any Apex code that has access to the class, not just the Apex in the same application
    • protected: means the method or variable is visible to any inner class in the defining Apex class as well as class that extend the Apex class
public class ExampleClass {
    String code = 'ABC123';         // defaults to private variable
    public String color = 'blue';   // accessible outside

    String getData1() {             // defaults to private method
        return 'getData1: ' + code + ' and ' + color;
    }

    public String getData2() {      // accessible outside
        return 'getData2: ' + code + ' and ' + color;
    }

    public String getData3() {
        return 'getData3: ' + getData1();
    }
}
ExampleClass ec = new ExampleClass();

System.debug(ec.color);         // blue
System.debug(ec.getData2());    // getData2: ABC123 and blue
System.debug(ec.getData3());    // getData3: getData1: ABC123 and blue

Interfaces

  • interface is used to require a class to implement specific methods
  • interface keyword: similar to a class but it uses the interface keyword and only contains method signatures
    • method signatures does not contain a body or access modifier and only consists of the name and any method parameter or return type
  • Apex classes use an interface by using the implements keyword. It is required to provide the body for all methods contained in the interface
  • A class can implement multiple interfaces
  • An interface separates the implementation logic of a method from the declaration
// Interface definition
public interface InterfaceName {
    // Method with signature only
    // and no access modifier
    Boolean methodName1();
    
    String methodName2();

}
// Class implementation of the interface
public class ClassName implements InterfaceName {
    public Boolean methodName1() {
        // code block here
        // return a Boolean value
    }
    public String methodName2() {
        // code block here
        // return a String value
    }
}
// Definition of an interface
public interface SalesOrder {
    Decimal calculateAmount(Decimalprice, Decimalquantity);
}

// An Apex class that implements the interface
public class B2BOrderimplementsSalesOrder {
    public Decimal calculateAmount(Decimalprice, Decimalquantity) {
        Decimaldiscount = 0.05;
        Decimalamount = price * quantity;
        returnamount - (amount * discount);  
    }
}

// Another Apex class that implements the interface
public class B2COrder implements SalesOrder {
    public Decimal calculateAmount(Decimalprice, Decimalquantity) {
        Decimalamount = price * quantity;
        returnamount;
    }
}
// Example implements multiple interfaces
public class MyApexClass implements MyInterfaceA, MyInterfaceB {
    // implementation here
}

Implementation Examples

  • Batch Apex implements an interface that enables it to execute complex, long-running operations over small batches of thousands of records
    • An Apex class that implements the Database.Batchable interface is required to use batch Apex
    • Batch Apex job: Database.executeBatch method can be used to execute a batch Apex job
      • requires two parameters: an instance of the batch Apex class and an optional scope (number of records to pass into the method)
global class MyBatchApexClass implements Database.Batchable {
    global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {
        // ...
    }
    global void execute(Database.BatchableContext bc, List<sObject>) {
        // ...
    }
    global void finish(Database.BatchableContext bc) {
        // ...
    }
}
  • Schedulable Apex is Batch Apex that can be scheduled by implementing an interface that enables it to be scheduled as an Apex job
    • Scheduled job: scheduled to run at specific times or intervals
    • Required Interface: implement the Schedulable interface
    • Scheduling Apex: use the Schedule Apex page in Setup or System.schedule method to specify the schedule
global class MyScheduledBatchApexClass implements Schedulable {

    global void execute(SchedulableContext sc) {

        MyBatchApexClass batch = new MyBatchApexClass();
        Database.executebatch(batch);
    }
}
  • Queueable Apex allows you to add asynchronous jobs to the queue and monitor them. It offers enhancements over @future methods
    • Job ID: Use System.enqueueJob() to submit a job and receive a Job ID that you can use to monitor progress
    • Non-primitive types: Queueable classes can contain non-primitive member variables like sObjects or custom Apex classes
    • Chain jobs: chain jobs together by having one Queueable job start a second job
public class MyQueueableApexClass implements Queueable {
    public void execute(QueueableContext qc) {     
        // perform processing logic...   
        // chain a second Queueable job     
        System.enqueueJob( new MyOtherQueueableApexClass() );
    }
}
  • Transaction Finalizer implements an interface which enable them to attach actions to a queueable Apex job
    • Use case: re-enqueue an Apex job when its operation failed
  • Following shows a simple Apex class example that implements Finalizer and Queueable interfaces and illustrates how the finalizer can be used to retry a failed job
public class RetryJob implements Finalizer, Queueable {
    // Queueable implementation
    public void execute(QueueableContext ctx) {
        Finalizer finalizer = newRetryJob(); // instantiate the finalizer
        System.attachFinalizer(finalizer); // attach the finalizer to the current queueable job   
        // perform some operations here
        // and then ...
        while (true) {
            // hit limits so that the job fails in this example   
        }
    }

    // Finalizer implementation
    public void execute(FinalizerContext ctx) {
        String parentJobId = '' + ctx.getAsyncApexJobId(); // get Apex job Id
        System.debug('Begin: executing finalizer attached to queueable job: ' + parentJobId);
        
        // if the job was not successful, re-enqueue the job that the finalizer is attached to
        if (ctx.getResult() == ParentJobResult.SUCCESS) {
            System.debug('Parent queueable job ' + parentJobId + ' completed successfully');
        } else {
            System.debug('Parent queueable job [' + parentJobId + '] failed due to: ' + ctx.getException().getMessage());
            String newJobId = '' + System.enqueueJob(newRetryJob()); 
            // retry fails after 5 times when it hits the chaining limit
        }
    }
}

Interface Scenarios

See FoF Slides