How can I dynamically execute classes in Apex? What if I don't know at compile time which class should be instantiated? Rather, I would like to decide at runtime which class would be appropriate to instantiate based on a variety of conditions.
3 Answers
Here is a bare-bones example of leveraging the Class Factory design pattern in Apex. It's an interface-based approach that permits dynamic class instantiation. The only requirement is to implement the interface method(s) but the rest of the class internals participating in the class factory can be completely unique (Class1, Class2, etc.)
ClassFactory:
public with sharing class ClassFactory
{
// Class Factory template
public interface IClassFactory
{
void processWork();
}
// Class Factory base class
public virtual class ClassFactoryBase
{
// ... Shared methods go here
}
// Process work
public static void processAllWork()
{
ClassFactoryManager cfm = new ClassFactoryManager();
cfm.newClassInstance('ClassFactory.Class1').processWork();
cfm.newClassInstance('ClassFactory.Class2').processWork();
cfm.newClassInstance('ClassFactory.Class3').processWork();
cfm.newClassInstance('ClassFactory.Class4').processWork();
}
// Class1
public class Class1 extends ClassFactoryBase implements IClassFactory
{
public void processWork()
{
// ... Class-specific work goes here
}
}
// Class2
public class Class2 extends ClassFactoryBase implements IClassFactory
{
public void processWork()
{
// ... Class-specific work goes here
}
}
// Class3
public class Class3 extends ClassFactoryBase implements IClassFactory
{
public void processWork()
{
// ... Class-specific work goes here
}
}
// Class4
public class Class4 extends ClassFactoryBase implements IClassFactory
{
public void processWork()
{
// ... Class-specific work goes here
}
}
}
ClassFactoryManager: (modified - thanks for the suggestion Peter)
public with sharing class ClassFactoryManager
{
public ClassFactoryManager(){}
// Return the appropriate class instance based on className
public ClassFactory.IClassFactory newClassInstance(String className)
{
Type t = Type.forName(className);
return (ClassFactory.IClassFactory) t.newInstance();
}
}
Ahhhh.... much better.
-
1Not that your answer is bad, but I was hoping the answer would involve something native to Apex that would accept a string and create a class from that like Java's Class.forName() with newInstance() instead of the if/elses. Sep 3, 2012 at 20:06
-
1I completely agree Peter. Reviewing the following post: blogs.developerforce.com/developer-relations/2012/05/… I think I may be able to modify this pattern by casting into an interface instead of explicit class names which should permit purely dynamic instantiation. I'll post my findings later.– AdamSep 3, 2012 at 20:28
-
-
-
Looks good @Adam ! I am waiting to get complete Java style reflection in Apex, so that more framework driven code could be written here. Sep 4, 2012 at 2:58
With the Callable interface that was introduced in Winter '19 you can now build a light weight interface for the methods you want to dynamically call from a class. Combining that with the Type class you have the option to instantiate the class dynamically and call any methods you expose in the call() method of the class dynamically.
The example below is from the callable interface docs which highlights both dynamic instantiation of classes and calling of methods.
Example class you want to dynamically call
public class Extension implements Callable {
// Actual method
String concatStrings(String stringValue) {
return stringValue + stringValue;
}
// Actual method
Decimal multiplyNumbers(Decimal decimalValue) {
return decimalValue * decimalValue;
}
// Dispatch actual methods
public Object call(String action, Map<String, Object> args) {
switch on action {
when 'concatStrings' {
return this.concatStrings((String)args.get('stringValue'));
}
when 'multiplyNumbers' {
return this.multiplyNumbers((Decimal)args.get('decimalValue'));
}
when else {
throw new ExtensionMalformedCallException('Method not implemented');
}
}
}
public class ExtensionMalformedCallException extends Exception {}
}
Unit test demonstrating the dynamic calling
@IsTest
private with sharing class ExtensionCaller {
@IsTest
private static void givenConfiguredExtensionWhenCalledThenValidResult() {
// Given
String className = 'Extension'; // Variable to demonstrate setting class name
String methodName = 'multiplyNumbers'; // Variable to demonstrate setting method name
Decimal decimalTestValue = 10;
// When
Callable extension = (Callable) Type.forName(className).newInstance();
Decimal result = (Decimal) extension.call(methodName, new Map<String, Object> { 'decimalValue' => decimalTestValue });
// Then
System.assertEquals(100, result);
}
}
Now this is possible in apex using Tooling API.
> ToolingAPI x = new ToolingAPI(); ToolingAPI.ExecuteAnonymousResult
> toolingResult = x.executeAnonymousUnencoded("Your apex code as a
> string here");
Please refer blog post - http://codefriar.wordpress.com/2014/10/30/eval-in-apex-secure-dynamic-code-evaluation-on-the-salesforce1-platform/
ClassFactory.IClassFactory
interface?