You can't cast ANY
to a more specific type, so you have to actually obtain the type that you want to return. Without proper reflection, you have to tell the function what type you'd like to return. There's several ways you can do that, but here's one way:
public class Parser {
public static List<List<Object>> splitList(List<Object> items, Integer splitSize, Type destType) {
// Take our destination type and cast to generic
List<List<Object>> result = (List<List<Object>>)destType.newInstance();
// Get a copy of source list to obtain list type.
List<Object> protoList = items.clone();
protoList.clear();
// This is the list that will actually be added to result
List<Object> tempList = protoList.clone();
// A for loop with two counters.
Integer index = 0, count = 0, size = items.size();
while(index < size) {
tempList.add(items[index++]);
count++;
// Split size reached, add to result and make new list
if(count == splitSize) {
result.add(tempList);
tempList = protoList.clone();
count = 0;
}
}
// Add left-over partial
if(!tempList.isEmpty()) {
result.add(protoList);
}
return result;
}
}
You use the code like this:
List<List<String>> v =
(List<List<String>>)Parser.splitList(
myStringList,
5, List<List<String>>.class
);
It's a little messy, as you have to give it a primitive data type and then cast it back, but it works.
As an alternative, you might try the "out-param" approach:
public class Parser {
public static void splitList(List<Object> items, Integer splitSize, List<List<Object>> result) {
List<Object> protoList = items.clone();
protoList.clear();
List<Object> tempList = protoList.clone();
Integer index = 0, count = 0, size = items.size();
while(index < size) {
tempList.add(items[index++]);
count++;
if(count == splitSize) {
result.add(tempList);
tempList = protoList.clone();
count = 0;
}
}
if(!tempList.isEmpty()) {
result.add(tempList);
}
}
}
This one can't incur the run-time exception if destType isn't compatible with List<List<Object>>
and saves you a bit of casting. You can use it like this:
List<List<String>> v = new List<List<String>>();
Parser.splitList(myStringList, 5, v);
The callee now becomes responsible for initializing the correct object type, but avoids the extra casting.
Take a look at Custom Iterators, you are mixing up Iterable
and Iterator
(crazy, I know). You need to return an Iterable
where you are using an Iterator
(you only implement the latter). Fortunately, you can just implement both.
class CustomIteratable implements Iterable<SObject>, Iterator<SObject>
{
public Iterator<SObject> iterator() { return this; }
public Boolean hasNext() { /*implementation*/ }
public SObject next() { /*implementation*/ }
}
Best Answer
You've got the right idea, just a technicality in one direction.
I try and put a familiar analogy in my head for these things, eg:
You can get lucky at runtime if and only if you cast (Account) something that really is an Account.
If apex unilaterally permitted
A
toB
casting at runtime, theB
behaviours could be very ill defined.His stuff that makes him a
B
(properties etc) could be missing. Exception thrown, surprise averted ;)