[SalesForce] Dynamically assign the dataType of a Set

I need to dynamically assign a datatype to a Set.

Set<dataType> elemensSet = new Set<dataType>();

Is this possible?

Best Answer

This is possible...to an extent (I do this myself in the trigger framework that I developed).

Apex doesn't really have a robust system for reflection/introspection, but there is the 'Type' class. Specifically, the Type.forName() and Type.newInstance() methods.

forName() (a static class method) allows you to pass in a type name (SObject type names, Apex class names, Apex interface names, etc...) as a string, and get a Type instance as a result.

newInstance() (an instance method of the Type class) allows you to, well, create a new instance of that type. This method returns a generic Object, and thus really needs to be explicitly type-cast to be of any use.

It's that required type-casting that makes this solution not as useful/powerful as it could be. As-is, this feature is mostly suited to making object factories that cast a child (apex) class as an instance of its parent class, or to cast a class as an instance of an interface.

That said, there is a small amount of trickery that we can pull off with collections.

List<SObject> sobjList;
Account testAcc = new Account(Name = 'My Test Account');
String listType = 'Account';

// Part of the 'trick' that we can do is determine the exact type of List that we want
//   at runtime.
// In my own trigger framework, I use something similar to abstract out trigger context
//   variables (to make isolating the testing of various framework components
//   from actually running triggers/performing dml possible)
sobjList = (List<SObject>)Type.forName('List<' + listType + '>').newInstance();
sobjList.add(testAcc);

// The point of this code is to show that we can down-cast from a generic type
//   to a concrete type.
// This is the other part of the 'trick'
List<Account> testImplicitDownCastList = sobjList;
Account otherTestAcc = (Account)sobjList[0];
otherTestAcc = testDownCastList[0];

system.debug('generic list: ' + sobjList);
system.debug('down-cast: ' + testDownCastList);

It should be noted that implicit down-casting (the most useful type) like I do in my example only applies to Lists (not Map, nor Set), and will break as soon as you would add something that isn't an Account to sobjList.

The reasons behind that are complex, and are explored a bit more in depth in one of the questions that I myself have asked: Is there a specific reason why we can't upcast Sets?