8

I have a List<sObject> genericList in scope in a method. The list was created and populated elsewhere and passed in as a parameter.

It could be:

  1. a List<sObject> that is homogeneous. E.g. All the records are Accounts.
  2. a List<sObject> that is heterogeneous. E.g. Some of the records are Accounts and some are Contacts.
  3. a List<Account> where all the records are forced to be Accounts.

How can I safely determine what I'm actually dealing with in the method?

I initially used instanceOf to check the List type, but I'm getting the following error where I expected the List to only contain Accounts. E.g:

List<sObject> genericList = new List<sObject>();
genericList.add(new OpportunityLineItem()); // Contrived example, but it shows the error
if(genericList instanceOf List< Account>) {
    List<Account> accounts = (List< Account>)genericList;
    for(Account a : accounts) {
        System.debug(a);
    }
}

System.QueryException: Dynamic query yields incompatible SObject type OpportunityLineItem for loop variable of type Account


I've found that I can't rely on instanceOf to provide clues about what it is. E.g.

List<sObject> genericList = new List<sObject>();
System.debug(genericList instanceOf List<sObject>); //true
System.debug(genericList instanceOf List<OpportunityLineItem>); //true
System.debug(genericList instanceOf List<Account>); //true

All those debug statements will return true, regardless of what the list actually contains.

However, if it is a List of a concrete type then I can determine that it isn't:

List<sObject> genericList = new List<Account>();
System.debug(genericList instanceOf List<sObject>); //true
System.debug(genericList instanceOf List<OpportunityLineItem>); //false
System.debug(genericList instanceOf List<Account>); //true

Another option is to use the List<X>.getSObjectType() method. If it is a concrete List that that method will return the Schema.SObjectType. If it is a List<sObject> then I get back null.

5

I believe I need to rely mostly on the List<X>.getSObjectType() method. At least as a first step.

If it doesn't return a null Schema.SObjectType then I can use that to determine the type.

If it does return null I've got more work to do. The type of each sObject in the list needs to be checked to see if they are all the same.

void processSObjectList(List<sobject> genericList) {

    Schema.SObjectType soType = genericList.getSObjectType();
    if(soType == null) {
        // It's a generic List<sObject>. Need to check individual record types
        Schema.SObjectType commonSObjectType = null;
        for(SObject so : genericList) {
            if(commonSObjectType == null) {
                commonSObjectType = so.getSObjectType();
            } else if (commonSObjectType != so.getSObjectType()) {
                // Found multiple sObject types. The List is heterogeneous
                System.debug('Found ' + so.getSObjectType() + '. Prior: ' + commonSObjectType);
                commonSObjectType = null;
            }
        }

        if(commonSObjectType != null) {
            // Lets make it a concrete list using https://salesforce.stackexchange.com/a/88385/102
            String listType = 'List<' + commonSObjectType + '>';
            List<SObject> typedList = (List<SObject>)Type.forName(listType).newInstance();
            typedList.addAll(genericList);
            // Call this method recursively with the now concrete list.
            System.debug('Got a typed list of : ' + commonSObjectType);
            processSObjectList(typedList);
        } else {
            //Deal with heterogeneous list
            System.debug('Deal with heterogeneous list');
        }
    } else if(soType == Account.sObjectType && genericList instanceOf List< Account>) {
        List<Account> accounts = (List< Account>)genericList;
        for(Account a : accounts) {
            System.debug('Processing Account: ' + a);
        }
    } else if(soType == OpportunityLineItem.sObjectType && genericList instanceOf List<OpportunityLineItem>) {
        List<OpportunityLineItem> olis = (List<OpportunityLineItem>)genericList;
        for(OpportunityLineItem oli : olis) {
            System.debug('Processing OLI: ' + oli);
        }
    } else {
        System.assert(false, 'BANG! Unknown list type:' + soType);
    }
}

processSObjectList(new List<Account>{new Account()});
processSObjectList(new List<OpportunityLineItem>{new OpportunityLineItem()});
processSObjectList(new List<sObject>{new OpportunityLineItem()});
processSObjectList(new List<sObject>{new OpportunityLineItem(), new Account()});

I'm converting the generic List<sObject> to a strongly typed list using the method from Apex Cast sObject list dynamically to a specific sObject Type.

  • 1
    I've had related issues: SObject[] sobjects = FilterTool.with(filterCriteria).filter(myAccounts); and if filter criteria yields no matches, sobjects is empty but sObjects.getSobjectType() is null. To get around this (as the FilterTool was a managed pkg), I ended up doing: SObject[] sobjects = new List<Account> (FilterTool.with(filterCriteria).filter(myAccounts)); to guarantee I always had an sobjectType - even on empty lists – cropredy Mar 1 at 2:18

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.