Declare variables, constants, methods, and use modifiers and Apex interfaces.
These are technical notes I compiled while studying using Focus on Force, a company that provides Salesforce training and certification prep services.
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
- Apex provides various data types for variables, constants, expressions, and expression operators for initializing and accessing data
- 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
- Stores a collection of binary data as a single object.
- 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
- Declared using
- 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
- Declared using the
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()
, andset()
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()
andremove()
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
- Ex:
- 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 aString
Strings
can be assigned to anID
, 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 validID
- Variable of
- Lower (in terms of precision) numeric types can be assigned to higher types without explicitly converting the data type. Precision hierarchy:
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
orprotected
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
()
- If no parameters, use empty parentheses
static
keyword: used to define a static method which does not require an instance of a class to runoverride
keyworkd: used to override a method in a class defined as virtual or abstract
- Access modifiers: such as
- 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
orprivate
, 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 namespaceglobal
: 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 applicationprotected
: 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 theinterface
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)
- An Apex class that implements the
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
- Job ID: Use
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