Difference in behaviour of List vs Set in Apex

apex

I have read this SO post, but I am asking a slightly different question:

I would like to be able to write a method that can accept a Set with any parameterized type.

e.g. myMethod(Set<Object> genericSet)

If I were wanting to do the same thing with a List, then I could using List<Object> because the following is possible:

List<Object> genericList = new List<String>();

In other words List<Object> can act as an abstraction of List<String>.

However, I can't use Set<Object> as a method parameter and expect to be able to pass in Set<String> because the following throws a compilation error:

Set<Object> genericSet = new Set<String>();

I'm not sure why this is. Possibly something to do with the fact that different types have different hashCode() functions?

Alternative

As an alternative, I could just have my parameter be of type Object, which would allow me to pass in any type of Set<?>. However, the problem with this is that I need to check that the argument passed in is indeed a Set of some sort. Furthermore, if it is, then because generic sets aren't allowed, I would need to know the exact type so that I can cast it to the correct set type.

I can't use myArg instanceof Set because Set has to has some sort of parameterized type.

And because of the issue I outlined above, I can't use myArg instanceof Set<Object> because this will return false for everything except when the parameterized type really is Object.

Does anyone know how I might be able to get some sort of generic behaviour for Sets?

Best Answer

The type system is oddly broken in a number of ways that they're also broken in Java, in addition to a unique set of problems in Apex that Phil W linked to.

However, if you're interested in a solution, you can indeed use sets and lists interchangeably through the use of Iterable. Unfortunately, it will require a cast every time you want to use it. Here's an Execute Anonymous script you can check out.

void printValues(Iterable<Object> v) {
    Object[] values = new Object[0];
    Iterator<Object> it = v.iterator();
    while(it.hasNext()) {
        values.add(it.next());
    }
    System.debug(values);
}
Set<String> setOfString = new Set<String>{'hello','world'};
printValues((Iterable<Object>)setOfString);
List<String> listOfString = new List<String>{'hello','world'};
printValues((Iterable<Object>)listOfString);

Notice how we can extract values from a list or set of any time if we cast it to Iterable first. Unfortunately, we still can't get to a "concrete" type dynamically, but you can use this on a case-by-case basis to work within the limitations of Apex.

Related Topic