The suggestion is to not use this type of polymorphism. You can't store specific collections in generic collections, but you can store generic collections in specific collections, which risks the odds of Bad Things™ happening. However, if you're willing to take the possibility of logic errors, you can always use the maps by only using generic collections.
Map<Object, SObject> keyValueMap = new Map<Object, SObject>();
keyValueMap.put("Hello", new Account());
keyValueMap.put("World", new Contact());
keyValueMap.put(42, new Opportunity());
You can still retrieve and set keys in various ways, but this means you'll frequently to either cast or use instanceOf on individual keys or values, which can make code more challenging to read. Also, if you decide to use List<SObject>
, you lose the ability to upsert as well.
You can workaround that by using some clever tricks, though:
public static Map<Object, List<SObject>> populateKeyValueMap(SObject[] records, Processor process, Type sobjectListType) {
Map<Object, List<SObject>> results = new Map<Object, List<SObject>>();
for(SObject record: records) {
Object key = process.findKey(record);
if(!results.containsKey(key)) {
results.put(key, sobjectListType.newInstance());
}
results.get(key).add(record);
}
return results;
}
...
Map<Object, List<SObject>> results = populateKeyValueMap(accountList, new MapByNameProcessor(), List<Account>.class);
for(Object key: results.keySet()) {
// Funny bug: no cast required here
Account[] accountNameList = results.get(key);
// more processing here
}
By using the dynamic System.Type, you can still have concrete lists, which lets you use upsert normally if you want to, as well as using static field references, etc. But, like I said, it would be easy to screw up, so if you plan on abusing the type system, make sure you unit test your code to death to avoid pesky runtime behavior.
Edit: Also, saw your comment; you could define the method as follows:
public static Map<Object, List<SObject>> groupByField(SObject[] records, SObjectField field, Type concreteListType) {
Map<Object, List<SObject>> results = new Map<Object, List<SObject>>();
for(SObject record: records) {
if(!results.containsKey(record.get(field))) {
results.put(record.get(field), concreteListType.newInstance());
}
results.get(record.get(field)).add(record);
}
return results;
}
This is actually a method I do use, but you need to actually caution future developers about the dangers of the methods with comments.
SObject
vsList<SObject>
. Where are you getting the structure from? Are you trying to write a mapping/grouping utility?List<sObject>
toList<Account>
(!) but notsObject
toAccount
. I'll edit the question again to match the answers.