Find your content:

Search form

You are here

Why is my Set reference not able to point to a Set collection, when it works for Lists?

 
Share

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?


Attribution to: Alan Morey

Possible Suggestion/Solution #1

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));

Attribution to: dphil

Possible Suggestion/Solution #2

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 :)


Attribution to: Nathan Williams

Possible Suggestion/Solution #3

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.


Attribution to: Peter Knolle

Possible Suggestion/Solution #4

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


Attribution to: macslo
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/33069

My Block Status

My Block Content