7

Is it possible to dynamically create a primitive type? (without having a series of if-else blocks)

Here's the class I was using to test what I wanted to achieve:

    public class Test {
    public Test(){}

    public void Add(List<Object> a, String className)
    {
        System.Type at = Type.forName(className);

        a.add(at.newInstance());
    }
}

When I ran these lines, the first set worked, but the String set didn't:

Test nt = new Test();

List<Contact> cTest = new List<Contact>();
system.debug(cTest);
nt.Add(cTest,'Contact');
system.debug(cTest);

List<String> strTest = new List<String>();
system.debug(strTest);
nt.Add(strTest,'String');
system.debug(strTest);

For the String, I get the following error:

System.TypeException: String cannot be constructed

Is it possible to do something similar to the "System.Type.newInstance()" call with primitives, without using a series of if-elses?

NOTE: This is similar to this post, but I want to go in the opposite direction.

4
  • Did you see my post in that same thread (the lower portion of it from before it edited it) with the code I first provided from Sam Arjmandi? I think that code is probably what you're looking for.
    – crmprogdev
    Oct 10 '14 at 19:05
  • @crmprogdev - I did see it, but I'm hoping there is something that is part of the language that would accomplish it. Oct 10 '14 at 19:49
  • I guess I didn't understand your question. Are you wanting to dynamically create a new sObject then instantiate as a specific type of sObject by passing its type into the class as string? If so, I can provide you with code that will do that which doesn't use any if-else clauses.
    – crmprogdev
    Oct 10 '14 at 21:20
  • Tried the code I had and discovered that an instance of an sObject is never an instance of a String. In essence, I couldn't make a list of Strings from an instantiated sObject. I could create an account or any other kind of sObject I wanted with the code I had, but not a primitive.
    – crmprogdev
    Oct 10 '14 at 22:20
8

It's not possible to do what you want with primitive data types, as you've pointed out you get a runtime error:

System.TypeException: String cannot be constructed

You also get the same error doing this as well:

String str = new String();

This is the case for primitives and implies that they do not have a public constructor for construction in this way (remember, you assign primitives using a literal, e.g. String str = 'str';).

The only success i've had with this type of thing is to utilise JSON and the Object type, something like:

String expr = '{"value":false}';
Map<String,Object> exprMap = (Map<String,Object>)JSON.deserializeUntyped(expr);
Object myBool = exprMap.get('value');

expr = '{"value":""}';
exprMap = (Map<String,Object>)JSON.deserializeUntyped(expr);
Object myString = exprMap.get('value');

expr = '{"value":0.0}';
exprMap = (Map<String,Object>)JSON.deserializeUntyped(expr);
Object myDecimal = exprMap.get('value');

And even this could be prone to ambiguity of some data types.

2
  • That's a pretty clever way to do it - Though it doesn't quite do what I'm looking for, it's a cool trick to remember. Thanks for your help :) Oct 14 '14 at 16:34
  • No probs, the lack of reflection capabilities on SF is a real pain Oct 14 '14 at 16:40
0

I think what's below is the kind of code you were looking for.

public class DynamicSObjectCreation {
    public static sObject createObject(String typeName) {
        Schema.SObjectType targetType = Schema.getGlobalDescribe().get(typeName);
        if (targetType == null) {
            // throw an exception
        }

        // Instantiate an sObject with the type passed in as an argument
        //  at run time.
        return targetType.newSObject(); 
    }
}        

Test Class

@isTest 
public class DynamicSObjectCreationTest {

   static testmethod void testObjectCreation() {
        String typeName = 'Account';
        String acctName = 'Acme';

        test.startTest();
        // Create a new sObject by passing the sObject type as an argument.
        Account a = (Account)createObject(typeName);        
        System.assertEquals(typeName, String.valueOf(a.getSobjectType()));
        // Set the account name and insert the account.
        a.Name = acctName;
        insert a;

        // Verify the new sObject got inserted.
        Account[] b = [SELECT Name from Account WHERE Name = :acctName];
        system.assert(b.size() > 0);

       test.stopTest();
    }
}

When tested with a string passed to it, this method fails. I modified the code to create a list of type string and received a compile error message that essentially translated to: an instance of an sObject is never an instance of a String.

I've concluded that dynamic instantiation of primitive sObject doesn't work using schema describe based operations. That makes sense to me when I take time to pause to think about it. Obviously, an sObject of type string does not have a name nor does it have an Id that one can pass to it, like one can to a record on a Standard or Custom object. All an sObject of type string is going to have is an array of strings and the name of the list that's assigned to it when running as code. There's nothing else to make it unique. One can create it on the fly by simply declaring it. There's no need to use any kind of a schema call to create it. These will of course continue to have a unique type to identify them by.

If it was a record containing fields with nothing but strings, then it would be a custom object that would still have a unique Id associated with it. That is a different matter entirely and would be returned in a getGlobalDescribe method.

I think what you'll want to do is create a series of methods with if statements that call the correct method if the string matches the type of primitive sObject list you want to create. I believe I've seen similar code, but don't have it at hand at the moment and you seem to have that part handled anyway.

4
  • 1
    The problem lies in instantiating primitive data types (e.g. String, Decimal) not sObjects. Oct 13 '14 at 7:55
  • Yes, would agree with you that its a matter of not being able to instantiate one. If you do a schema describe, a primitive won't exist in an Org's description as they're not part of it's DB schema. I suspect that may be source of problem with instantiating one under SF. They're really only used in a transient manner by Apex as they're not part of the database's object record structure & syntax doesn't allow for instantiating them outside of that structure.
    – crmprogdev
    Oct 13 '14 at 15:51
  • That is just a matter of perspective. If you are a developer with a strong OOP background, sObjects are an anomaly. They have a constructor accepting named parameters and cannot be extended. If you are a Java developer, the primitives like String and Decimal are an anomaly. They do have methods (both instance and static) but cannot be instantiated. If you are a DB developer, classes and primitives are weird, because they are not represented in the schema. One must train himself to change those perspectives often to make the most of it. Oct 14 '14 at 8:47
  • @crmprogdev - I had a similar problem when I was first trying to figure this out - Being self taught, there wasn't a clear distinction in my mind between an sObject and an Object. It really is odd that they don't treat primitives as children of Object. Thanks for taking the time to look into this :) Oct 14 '14 at 16:44

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.