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

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?

Answer

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
Source : Link , Question Author : Alan Morey , Answer Author : Peter Knolle

Leave a Comment