Instantiating Apex class and accessing its properties dynamically

Shama Gurav
4 min readSep 18, 2023

--

Dynamic Apex is useful to write reusable and generic methods. This is handy if you are dealing with multiple apex types. Writing generic methods reduces code duplication, makes your code efficient and maintainable. Consider the following scenario:

‘ABC ltd’ has custom object “Inventory” which stores information about all types of Products. ‘ABC ltd’ wants to implement a solution where if Inventory category has changed then schedule a job to sync all the data related to inventory product with external data source. However, if Inventory record owner is changed then schedule a job to notify all the team members of the related account.

To schedule a job in salesforce, developers need to create an instance of apex class and pass it to System.schedule method. Hence, Developers will need to create instance of the Apex class depending on what is changed on Inventory. This will work well but the code won’t be reusable because it will be tightly coupled with this specific requirement.

On the contrary, developers can use dynamic Apex to implement a generic method which will use the dynamically provided apex class name to create an instance of the Apex class and schedule a job. Then, this generic method can be reused and won’t require any code changes.

Instantiate Apex class dynamically:

Dynamically instantiating Apex class involves below steps:

  1. Create an Apex Class and Apex Interface.
  2. Implement the interface in the Apex class.
  3. Get type of the Apex class where it should be instantiated.

Get the type of Apex class by using “Type” class (Namespace:System) as below,

Type t = Type.forName(<className>);

2. Instantiate the Apex class type by using interface.

<interfaceName> interfaceObject = (intefaceName)t.newInstance();

Example:

The following sample code shows how to instantiate an Apex class based on the name. A typical use case of this scenario is when you need to invoke the methods with respect to a class which you will provide at run time.

In this sample code, IMachine represents the interface that the ‘Robots’ and ‘Clocks’ classes implements. The ‘InvokeApexDynamically’ class contains the code sample that invokes the methods implemented in apex classes ‘Robots’ and ‘Clocks’ respectively.

This is the IMachine interface.

public Interface IMachine {
string getCategory();
date getManufacturingDate();
}

This is the implementation of the IMachine interface.


public class Robots implements IMachine{

public String getCategory() {
return 'Chatbots';
}
public date getManufacturingDate() {
return system.date.today();
}
}
public class Clocks implements IMachine{
public String getCategory() {
return 'Analogue';
}
public date getManufacturingDate() {
return system.date.today()+1;
}
}

Below Apex class “InvokeApexDynamically” implements the method “invokeCustomImpl”. This Method gets the name of the class that implements the IMachine interface. It then instantiates this class by getting the corresponding type and calling the newInstance method. Next, it invokes the methods implemented in Robots Apex class and Clocks Apex class respectively.

public class InvokeApexDynamically {
public static void invokeCustomImpl()
{
String strClassName='Robots';
// Get the Type corresponding to the class name.
Type t = Type.forName(strClassName);

// Instantiate the type.
// The type of the instantiated object.
// is the interface.
IMachine m = (IMachine)t.newInstance();

// Call the methods that have a custom implementation.
System.debug('Category of ' + strClassName +' : ' + m.getCategory());
System.debug('Manufacturing date of ' + strClassName +' : ' + m.getManufacturingDate());

strClassName='Clocks';
// Get the Type corresponding to the class name.
t = Type.forName(strClassName);

// Instantiate the type.
// The type of the instantiated object.
// is the interface.
m = (IMachine)t.newInstance();

// Call the methods that have a custom implementation.
System.debug('Category of ' + strClassName +' : ' + m.getCategory());
System.debug('Manufacturing date of ' + strClassName +' : ' + m.getManufacturingDate());
}

}

Output of executing above method is as below,

09:54:47:054 USER_DEBUG [14]|DEBUG|Category of Robots : Chatbots
09:54:47:054 USER_DEBUG [15]|DEBUG|Manufacturing date of Robots : 2023-09-18 00:00:00
09:54:47:126 USER_DEBUG [27]|DEBUG|Category of Clocks : Analogue
09:54:47:126 USER_DEBUG [28]|DEBUG|Manufacturing date of Clocks : 2023-09-19 00:00:00

Accessing Apex Class Properties dynamically at run time :

Put and Get methods of sObject in Apex gives a flexibility of setting its properties and retrieving the values from properties respectively at runtime. This is useful when you are writing generic and reusable code. For example, if Developers need to get the value of ‘Name’ field from Account object, then they can use get method to retrieve the value as follows,

String varName=AccObj.get(‘Name’);

In the similar manner, if Developers need to set the property ‘Name’ with value ‘ABC Pvt. Ltd.’ then they can do the following,

AccObj.put(‘Name’,’ABC Pvt. Ltd.’);

This functionality is not readily available for Apex class objects. However, it can be achieved by,

  1. Json Serializers and Deserializers.
  2. Getters and Setters.

JSON Serializers and Deserializers:

This is the Apex class ‘CommercialMachines’ for which we need to set properties and retrieve the same at run time.

public class CommercialMachines {
String Category;
Integer Model;
}

Below Apex class ‘AccessApexPropertyDynamically’ implements ‘AccessCustomImpl’ method which is using JSON.deserializeUntyped() method to get the Apex class object in Map<String,Object> format. Next, It uses this object to set and get the properties at runtime. This Map object then gets converted into apex class object using JSON.deserialize().

public class AccessApexPropertyDynamically {
public static void AccessCustomImpl()
{
CommercialMachines ComMachine=new CommercialMachines();
Map<string,Object> mapCommMachine= (Map<String,Object>)JSON.deserializeUntyped(JSON.serialize(ComMachine));
//Set values for apex class property at run time.
mapCommMachine.put('Category','Jukebox');
mapCommMachine.put('Model','Gabel Kuro');

//get values for apex class property run time
system.debug(mapCommMachine.get('Category')); //'Get Category value at run time.'
system.debug(mapCommMachine.get('Model'));

//Convert the map again to Apex class object
CommercialMachines ComMachineFinal=(CommercialMachines)JSON.deserialize(JSON.serialize(mapCommMachine),CommercialMachines.class) ;
System.debug('CommercialMachines object ' + ComMachineFinal);
}
}

Output of the method “AccessCustomImpl” will look like below,

19:34:25:026 USER_DEBUG [8]|DEBUG|Jukebox
19:34:25:026 USER_DEBUG [9]|DEBUG|Gabel Kuro
19:34:25:030 USER_DEBUG [11]|DEBUG|CommercialMachines object CommercialMachines:[Category=Jukebox, Model=Gabel Kuro]

Using Getters and Setters:

In below Apex class, we are updating private Map MachineMap in set method. We can then retrieve the property values similar to get method.

public class CommercialMachines {
private Map<string,object> MachineMap=new Map<string,object>();
public String Category
{get;
set
{
this.Category = value;
this.MachineMap.put('Category', value);
}
}
public String Model
{
get;
set
{
this.Model = value;
this.MachineMap.put('Model', value);
}
}
public Object get(String PropertyName)
{
return MachineMap.get(PropertyName);
}
}

Developers can set and get the values of the apex class “CommercialMachines” properties as below,

CommercialMachines obj=new CommercialMachines();
obj.Category='Jukebox';
obj.Model='LINK';
system.debug(obj.get('Category'));
System.debug(obj.get('Model'));

Output of the above code will be,

16:56:52:021 USER_DEBUG [5]|DEBUG|Jukebox
16:56:52:022 USER_DEBUG [6]|DEBUG|LINK

References:

https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_type.htm

--

--

No responses yet