Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

Take the following line of code:

Set<Object> accounts = new Set<Account>();

When I try to execute this in developer console I get an error Illegal assignment from SET to SET

Same error applies if I use a non-SObject type:

Set<Object> strings = new Set<String>(); // Illegal assignment from SET to SET

What has me confused is that we are able to do this with Lists, so the following is valid code:

List<Object> accounts = new List<Account>();
List<Object> strings = new List<String>();

I ran into this problem while trying to write a helper class as follows with an isEmpty() method I could call and pass it any type of collection.

class Util {
    public static Boolean isEmpty(Set<Object> collection) {
        return collection.isEmpty();
    }

    public static Boolean isEmpty(List<Object> collection) {
        return collection.isEmpty();
    }

    public static Boolean isEmpty(Map<Object, Object> collection) {
        return collection.isEmpty();
    }
}

By the way, a similar problem occurs for Maps:

Map<Object, Object> accounts = new Map<Id, Account>(); // Illegal assignment from MAP to MAP
Map<Object, Object> strings = new Map<String, String>(); // Illegal assignment from MAP to MAP

Anyone know why this is the way it is? Are there some technical reasons? Is there any way around this?

share|improve this question
    
Very interesting it's different between Sets and Lists... My understanding is that Sets and Lists differ in only two ways: A list is ordered and can have dups while a set is unordered and will not have dups... –  Nathan Williams Apr 17 at 21:41
add comment

4 Answers 4

I couldn't find any exact documentation, but I suspect that it has to do with SOSL. SOSL returns a List whose elements are Lists themselves of different types. For example:

List<List<SObject>> searchList = peformSosl();

That means that List<Account>, List<Contact>, etc. must be subtypes of List<sObject> in order to be on the SOSL result List.

In the absence of SOSL, I would've thought the List scenario would not be possible. I understand that when it comes to the element types on the Lists an Account IS-A Object, but I didn't think that a List<Account> IS-A List<Object>.

Also, it is unsafe to me. Consider the following:

List<Object> accts = new List<Account>(); 
accts.add(new Contact(LastName='oops'));`

That compiles and there is no casting whatsoever. Run it and you get an error. Contrast that with the following, which catches the error at compile time.

List<Account> accts = new List<Account>();
accts.add(new Contact(LastName='noCompile'));

If you needed a List that could contain multiple types safely you can just instantiate a new List<Object>.

List<Object> objs = new List<Object>();
objs.add(new Contact(LastName='ok'));
objs.add(new Account(Name='ok2'));

Java, which definitely has a big influence on the design of Apex would not allow the unsafe List creation. The following in Java would not compile and would result in an incompatible type error.

List<Object> objs = new ArrayList<String>();

Sets, and Maps aren't returned in SOSL, so they don't have to suffer this fate and their type safety can be enforced at compile time.

share|improve this answer
2  
This comes up in C# generics and is called Covariance and contravariance in computer science. –  Daniel Ballinger Apr 18 at 10:04
add comment

This is quote from docs:

Uniqueness of set elements of user-defined types is determined by the equals and hashCode methods, which you provide in your classes. Uniqueness of all other non-primitive types is determined by comparing the objects’ fields.

Last sentence is important. Different types of objects have different fields.

Furthermore you shouldn't sObject for example Accounts in set and as a key in maps.

Example:

Set<Account> accountSet = new Set<Account>();

Account a = new Account(name = 'zyx');
accountSet.add(a);

insert a; //id will be assigned 

system.debug(accountSet.contains(a)); 
//return false, because hashCode has changed after insert

In List it is perfect valid, because list doesn't use hashCodes.

https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_set.htm#apex_methods_system_set

share|improve this answer
add comment

I don't have a definitive answer, but I could make a guess.

My assumption would be that each time you are adding an item to a Set (Map key is a Set which is why it works the same way) that it compares the item you are adding with each of the others in the Set to make sure it is unique. It doesn't have a way to compare uniqueness between different types of objects and so requires each object in a set to be of a single type.

That being said, try seeing if this works. You could simply do this instead:

List<SObject> accounts = new List<SObject>();
Account acc = new Account();
accounts.add((SObject) acc));
share|improve this answer
add comment

Your Util class is actually completely unnecessary... Why are you trying to build functions that do the same thing as set.isEmpty() list.isEmpty() or map.isEmpty()? All that class accomplishes is turning set.isEmpty() in your code into Util.isEmpty(set), so if anything it adds characters to your code... Kill your util class and move on :)

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.