9

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.

  • Adam, I stumbled across this thread as I've implemented something similar but am having an issue with this pattern. Wanted to see if you've run across it. The FactoryManager of course returns an instance of ClassFactory.IClassFactory, but that object doesn't seem to have access to the public methods in the ClassFactoryBase (since it's the interface). I'm getting "method does not exist or incorrect signature". Did you cast this into a concrete class or something? Seems like it still requires if/then to cast to the correct class. Thanks! – Jeff Douglas Mar 17 '13 at 9:18
  • @JeffDouglas How about annotating the shared methods into the ClassFactory.IClassFactory interface? – bigassforce Mar 17 '13 at 21:24
  • Hi Jeff - I just verified this and it works perfectly. I created a public void method called doSomething() in base and then invoked it from processWork() in the Class1 subclass of base. If you want I'd be happy to look at your code via email/screen share or something. Let me know. – Adam Mar 18 '13 at 3:21
15

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.

  • 1
    Not 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. – Peter Knolle Sep 3 '12 at 20:06
  • 1
    I 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. – Adam Sep 3 '12 at 20:28
  • Edited and tested - it works. – Adam Sep 3 '12 at 20:37
  • Looks really good! – Peter Knolle Sep 3 '12 at 21:50
  • Looks good @Adam ! I am waiting to get complete Java style reflection in Apex, so that more framework driven code could be written here. – Abhinav Gupta Sep 4 '12 at 2:58
1

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/

0

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.