10

I am trying to create some junction records via callout (in this case, SkillUser records). Since I am doing so from a trigger, I need to use a @future annotated method. The Apex Developer Guide states the following about Future Methods (emphasis mine):

Methods with the future annotation must be static methods, and can only return a void type. The specified parameters must be primitive data types, arrays of primitive data types, or collections of primitive data types. Methods with the future annotation cannot take sObjects or objects as arguments.

It seems to me that Map<Id, Set<Id>> is a collection of primitive data types. However, the compiler states this parameter type is unsupported, despite the fact some other Map types are supported. Why is that?

Compiles

@future
public static void doStuff(Map<Id, Id> input) { }
@future
public static void doStuff(Set<Id> input) { }
@future
public static void doStuff(List<Id> input) { }

Compile Fail

@future
public static void doStuff(Map<Id, Set<Id>> input) { }

Unsupported parameter type Map<Id,Set<Id>>

I know I can use workarounds like JSON serialization, but I would like to understand why this type does not count as a collection of primitive data types.

  • Sounds like you're confusing the compiler... – EricSSH Jun 27 '17 at 17:15
  • 4
    Perhaps the documentation should have said a single level collection of primitive data types to emphasise the point. – Keith C Jun 27 '17 at 17:21
9

The list of primitives doesn't include Map, Set, or List. Therefore, what you have is a collection of non-primitives. They've also explicitly excluded Object, so you can't weasel your way around that limitation that way, either. You cannot include any nested collections at all, including things like List<List<String>> or Set<List<String>>, or any other combination of nested collections.

This restriction has to do with how future methods serialize their parameters; it literally cannot support complex data types. It was originally placed on future methods to limit the complexity of serialization, which is why salesforce allowed 50 future calls as opposed to five Batchable calls per transaction.

Since then, we've been given Queueable, which suggests that future should enjoy the same serializing features, but it hasn't been upgraded, and probably won't be for the foreseeable future, because we have a newer feature that gives this this ability already. If you need future-like behavior with complex types, use Queueable instead.

  • 1
    So basically the key and value both have to be primitives? You can't have a value which is a collection of primitives? – Adrian Larson Jun 27 '17 at 17:23
  • @AdrianLarson Correct. The implementation is true to the literal interpretation of the wording. You can't have collections of collections, because collections are not primitives. I agree that it's kind of deceptive, but the documentation is precisely correct. – sfdcfox Jun 27 '17 at 17:25
  • The workaround stuff isn't really relevant to me but this answer does explain why the type doesn't count as a collection of primitives. – Adrian Larson Jun 27 '17 at 17:35
  • What is the newer feature that has this ability already that you're talking about? – Dan Wooding Jun 27 '17 at 18:09
  • 2
    @DanWooding Queueable Apex. – sfdcfox Jun 27 '17 at 18:15
4

Technically, you are passing in a Collection of a Collection. While semantically, you could argue that this reduces to a Colleciton, this is not how the Apex compiler has chosen to interpret it.

The primitive data types listed here are:

  • Blob
  • Boolean
  • Date
  • Datetime
  • Decimal
  • Double
  • Id
  • Integer
  • Long
  • Object
  • String
  • Time

For the Apex compiler to accept a Map<Id,Set<Id>>, then Set<Id> would need to be one of these, not just Id itself (excepting Object as sfdcfox pointed out).

  • Except for "Object", actually. You can't use Object as a parameter type, or any collection that involves Object. They really don't want us to break things. – sfdcfox Jun 27 '17 at 17:32
  • Yes - true - "Object" is explicitly called out – Caspar Harmer Jun 27 '17 at 17:33
-1

"A map is a collection of key-value pairs where each unique key maps to a single value. Keys and values can be any data type—primitive types, collections, sObjects, user-defined types, and built-in Apex types."

a collection of collection is not a primitive data type..

  • 2
    Incorrect. A Map is a collection. Note that Map<Id, Id> is a valid parameter type. – Adrian Larson Jun 27 '17 at 17:13
  • A map<id, id> is a special case and in essence is the same as set<id> any more complex structure is not a primitive data type.. – Benjamin Pirih Jun 27 '17 at 17:18
  • 1
    I don't agree there is any basis for your claim that Map<Id, Id> is equivalent to Set<Id>. I didn't vote you down, but your line of thinking doesn't appear to be supported by the facts. – Adrian Larson Jun 27 '17 at 17:19
  • OK let me rephrase a simple map which consists on a key primitive and a value primitive data type is acceptable. A complex map which is key and collection is not a primitive data type.. hence you can do map<Id, string> but cannot do map<Id, set<string>> – Benjamin Pirih Jun 27 '17 at 17:22
  • 1
    SFDCFOX used the right language.. "you cannot have collection of non-primitives" map<id, string> is a collection of primitives.. map<id, set<string>> is not a collection of primitives but a collection of collections and that is not a primitive. – Benjamin Pirih Jun 27 '17 at 17:26

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.