I'm trying to build a general-purpose piece of functionality that can be added to batch classes. My class is Batchable
itself and it will take in an instance of another Batchable
. For each of the methods in the Batchable
interface, my class will do some extra work, then delegate to the other Batchable
. In doing so, I'm having trouble…
Something that stands out as very odd about the Batchable
interface is that the docs say you must implement:
global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {}
So the return type of the start()
method could be Database.QueryLocator
or Iterable<sObject>
. Which is not normally possible.
Most of the time, this doesn't matter, but if I have a reference to an instance of a Batchable
, it seems like I can't call the Database.QueryLocator
version. For example:
public with sharing class BatchableStartMethodTest implements Database.Batchable<SObject> {
public void execute(Database.BatchableContext bc, List<SObject> scope) {
}
public void finish(Database.BatchableContext bc) {
}
public Database.QueryLocator start(Database.BatchableContext bc) {
return null;
}
public static void doesntCompile() {
Database.Batchable<SObject> instance = new BatchableStartMethodTest();
Database.QueryLocator locator = instance.start(null);
}
public static void compiles() {
BatchableStartMethodTest instance = new BatchableStartMethodTest();
Database.QueryLocator locator = instance.start(null);
}
}
In this, the doesntCompile()
method gives a compilation error:
Error:(20, 31) Illegal assignment from System.Iterable to
Database.QueryLocator (20:31)
I can see what the error is getting at, but I can't see how to tell the compiler that I'm using the QueryLocator
version. I might try to define my own derived interface, but that's not possible either:
public interface BatchableQueryLocator extends Database.Batchable<SObject> {
Database.QueryLocator start(Database.BatchableContext bc);
}
Error:(7, 18) Method return types clash: System.Iterable vs
Database.QueryLocator from the type Database.Batchable (7:18)
Does this just mean that two return types in Batchable
is some weird special case in the compiler that I cannot deal with in the way I want to? I could create an interface that doesn't extend the existing Batchable
i.e.
public interface BatchableQueryLocator {
Database.QueryLocator start(Database.BatchableContext bc);
}
But that seems ugly because then I don't get full type-safety – I can't tell the compiler that my method expects something that implements both Batchable
and BatchableQueryLocator
. Any suggestions?
Best Answer
I solved this problem by using
Iterable<Object>
as the return type. Interestingly, Database.QueryLocator implements Iterable, so it actually does work the way may not expect. Here's the full implementation:To get QueryLocator to work right, you have to cast it:
The interfaces ObjectGenerator and ObjectAction allows a developer to specify different types of iterables or query locators. Here's an example execution:
Where RecordsFromQuery just returns a dynamic Database.QueryLocator, and UpdateRecords is a simple
Database.update(records, false);
(this version is simply meant to try and apply triggers to existing records).I have a more complicated framework that I'm working on, but I think this simple example should help you get where you're trying to go.