9

Edit: Have edited the title in the hopes that anyone in the future looking for help on a related topic can find this easily. Here is a great way to implement polymophism with batch classes: https://salesforce.stackexchange.com/a/156010/40851

I'm writing a few job classes: Job1, Job2, and so on.

global class Job1 implements Database.Batchable<sObject>, Database.Stateful {

    private String query;

    global Job1(String query) {
        this.query = query;
    }

    global void setQuery(String query) {
        this.query = query;
    }

    global String getQuery() {
        return query;
    }

    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext bc, List<Account> scope){
        // process each batch of records

    }    

    global void finish(Database.BatchableContext bc){
        // other logic goes here
    }    

}

I've created 5 such classes like this.

Next, I want to be able to use polymorphism and instantiate job objects as such:

Database.Batchable<sObject> j1 = new Job1();

Now, I'll do j1.setQuery('SELECT Name from Account');

However, doing this results in:

Method does not exist or incorrect signature: [Database.Batchable<SObject>].setQuery(String)

What am I doing wrong?

2
  • 1
    Shouldn't it be Job1 j1 = new Job1(); instead of Database.Batchable<sObject> j1 = new Job1();? I feel like if the setQuery method is only defined on the Job1 class, you'd have to be calling the method on an instance of a Job1
    – martin
    Jan 16, 2017 at 5:55
  • My end goal is to create something of an object factory, which is why I require the polymorphism.
    – coldspeed
    Jan 16, 2017 at 6:01

3 Answers 3

15

To consolidate your code, set up an abstract class:

public abstract class BaseJob implements Database.Batchable<SObject>, Database.Stateful {
    String query;
    public void setQuery(String query) {
        this.query = query;
    }
    public Database.QueryLocator start(Database.BatchableContext context) {
        return Database.getQueryLocator(query);
    }
    public abstract void execute(Database.BatchableContext context, SObject[] scope);
    public abstract void finish(Database.BatchableContext context);
}

Now, we can implement your JobX classes:

public class Job1 extends BaseJob {
    public override void execute(Database.BatchableContext context, SObject[] scope) {
        // Do whatever we need to do
    }
    public override void finish(Database.BatchableContext context) {
        // And finish up
    }
}

Finally, we just need to call your job:

BaseJob j = new Job1();
j.setQuery('select id from lead');
Database.executeBatch(j);

Or dynamically:

public static void doJob(Type className, String withQuery) {
    BaseJob j = (BaseJob)className.newInstance();
    j.setQuery(withQuery);
    Database.executeBatch(j);
}
2
  • 1
    Thanks! It worked perfectly. Am still learning the ropes at Salesforce, apologies if it was a silly question.
    – coldspeed
    Jan 16, 2017 at 6:08
  • 2
    @Shiva I'm glad I could help. It's definitely a useful pattern to know about, it can consolidate a lot of code.
    – sfdcfox
    Jan 16, 2017 at 6:09
3

There is an Object Oriented Programming concept.

If user has defined a constructor in a class. The default constructor doesn't work or can be called. If user defined constructor signature is same as default constructor. User defined constructor will override the default constructor.

So, you have defined a constructor:-

global Job1(String query) 
{
        this.query = query;
}

It takes argument type String. Because of this no argument constructor (default constructor) is removed from class definition.

If you want to use new Job(), define the constructor as below but make sure query need to be set:

global Job1() 
{
     // TO DO
}
2

For completeness, note that inheritance isn't always the best approach. Where you have common behaviour (methods), classes that support those methods can be identified via an interface:

global interface QueryConfigurable { 
    void setQuery(String query;
}

like this:

global class Job1 implements QueryConfigurable, ... {
    ...
}

Would result in a cast for this example, but in other cases could be a bit cleaner:

Job1 j = new Job1();
((QueryConfigurable) j).setQuery('select id from lead');
Database.executeBatch(j);

With this approach you can compose functionality using multiple small interfaces; inheritance is an all or nothing approach.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .