19

I have an abstract class called Process that I extend to create a list of processes (List<Process>); each in relation to an object. For now everything works as expected but I'm really hoping to create a very dynamic approach to these processes.

For example I also have a Process__c custom object, and with a SOQL statement I can get a list of all the Process_c records I need for a given context. These records contain the String name of a class that I want to instantiate, but for the life of me I can't seem to find a way to do this.

I've tried something like the following:

String s = 'Account';
List<Process__c> activeProcesses = [SELECT Name FROM Process__c WHERE Active__c = TRUE AND Object__c = :s];
List<String> processNames = Utils.Data.Convert.stringList(activeProcesses, 'Name');

Type t = Type.forName('Process');
List<Process> processes = new List<Process>();
for(String processName : processNames){
    Process p = t.newInstance(processName);
    processes.add(p);
}

The error I'm getting when attempting this is:

Method does not exist or incorrect signature: [Type].newInstance(String)

I really hope the harsh reality isn't that this is just not possible but if it is I need to know that as well, so any insight you have with this question would be greatly appreciated.

1
  • Yes thank you very much. Someday this may lead to a free public release, if so I'll be sure to give thanks there as well! Commented Aug 23, 2016 at 21:45

2 Answers 2

22

Your strategy will work, but your constructor must contain no parameters, and the same goes for your newInstance() call. You pass the name of the Type you want to construct into the Type.forName method.

Type customType = Type.forName('SomeClass');
SomeClass instance = (SomeClass)customType.newInstance();

You probably will want to implement an interface here as well. Something like:

public interface IProcess { void execute(); }
public class Process1
{
    public Process1()
    {
        // instantiation logic
    }
    public void execute()
    {
        // execution logic
    }
}

You would use the above as follows:

IProcess instance = (IProcess)Type.forName('Process1').newInstance();

You don't even really need to cache it for simple cases:

(IProcess)Type.forName('Process1').newInstance().execute();
6
  • How do I then pass it the extended class name that I want if not through there? Commented Aug 23, 2016 at 20:54
  • @Xtreme You pass it into Type.forName.
    – Adrian Larson
    Commented Aug 23, 2016 at 20:56
  • Awesome, got my temp example to work! Now to plug it into my project. Thanks so much! Commented Aug 23, 2016 at 20:58
  • 1
    Nice. Be sure to test it with a variety of Profiles, I had to work through some access issues last time I implemented something dynamic like this.
    – Adrian Larson
    Commented Aug 23, 2016 at 20:59
  • Thanks for that. Also the Process class is an Abstract because I have more than just method signatures defined in the class. From what I understand an Abstract is a type of Interface as well right? Commented Aug 23, 2016 at 21:01
16

If you do have a need to pass parameters to the dynamically created class, a way to do that is to create the class by using JSON.deserialize:

Type t = Type.forName('Process');
Process p = (Process) JSON.deserialize('{}', t);

so that if the class has e.g. fields x and y:

public virtual class Process {
    Integer x;
    String y;
}

you can set values in them like this when the instance is created:

// This could come from e.g. a configuration custom setting
String configClass = 'ProcessSubClass';
String configParameters = '{"x": 34, "y": "Hello world"}';

Process p = (Process) JSON.deserialize(configParameters, Type.forName(configClass));

assuming configClass is a sub-type of Process.

3
  • I'll have to remember this for the next time I have a project that involves using Type.forName(). I had previously resigned myself to the thought that I would always need to create a separate method to mirror a constructor if I needed to provide anything to a dynamically instantiated class. My only potential concern is with the performance of JSON.deserialize(). Have you run any tests to characterize the performance?
    – Derek F
    Commented Aug 23, 2016 at 22:44
  • @DerekF No I'm afraid I haven't; typically used this for limited numbers of configuration objects where the performance has been good enough.
    – Keith C
    Commented Aug 23, 2016 at 22:55
  • @DerekF I wondered about performance as well. Note too that you don't need to get the type dynamically. You can do JSON.deserialize(payload, ProcessSubClass.class) just as easily as using Type.forName for that second parameter.
    – Adrian Larson
    Commented Aug 23, 2016 at 23:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .