An interface in Apex is a named contract: a list of method signatures with no method bodies. Any class that implements the interface must provide a body for every method in it. Interfaces let unrelated classes promise that they support the same operations, which is the foundation of dependency injection, mocking, and almost every Salesforce framework type — Batchable, Queueable, Schedulable, Comparable.
Defining and implementing
// The contract
public interface Persistable {
void save();
Boolean isValid();
}
// One implementation
public class OrderRecord implements Persistable {
public void save() {
// body required
}
public Boolean isValid() {
return true;
}
}
// Another implementation, completely different class
public class InvoiceRecord implements Persistable {
public void save() {
// entirely different logic
}
public Boolean isValid() {
return false;
}
}
Now you can write code that handles either type uniformly:
public static void persistAll(List<Persistable> items) {
for (Persistable p : items) {
if (p.isValid()) {
p.save();
}
}
}
The persistAll method has no idea whether it’s dealing with OrderRecord or InvoiceRecord — it only knows the methods on the interface. That decoupling is the whole point.
Built-in Salesforce interfaces
Apex ships with a long list of interfaces the platform uses to dispatch your code. You implement them; the platform calls them.
| Interface | Implement when you want to… | Required methods |
|---|---|---|
Database.Batchable<sObject> | Run a batch job | start, execute, finish |
Queueable | Run an asynchronous job | execute(QueueableContext) |
Schedulable | Run a job on a schedule | execute(SchedulableContext) |
Comparable | Sort custom objects in a List.sort() | compareTo(Object) |
Iterable<T> / Iterator<T> | Implement a custom iterator | iterator(), hasNext(), next() |
Auth.AuthProviderPlugin | Build a custom OAuth provider | Several |
Messaging.InboundEmailHandler | Handle inbound emails | handleInboundEmail(...) |
System.Callable | Generic invocation entry point | call(String, Map<String, Object>) |
The pattern is always the same: the platform doesn’t care about your class’s name. It looks for the interface, finds your execute() (or whatever’s required), and calls it.
public class NightlyCleanup implements Database.Batchable<sObject>, Schedulable {
public Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([SELECT Id FROM Stale_Record__c WHERE Created__c < :System.now().addDays(-30)]);
}
public void execute(Database.BatchableContext bc, List<Stale_Record__c> scope) {
delete scope;
}
public void finish(Database.BatchableContext bc) {
// notify, log, etc.
}
public void execute(SchedulableContext sc) {
Database.executeBatch(this, 200);
}
}
A class can implement multiple interfaces simultaneously. This is the Apex equivalent of Java’s multiple inheritance for interfaces.
Interface vs abstract class
Apex supports both. The difference:
| Aspect | Interface | Abstract Class |
|---|---|---|
| Method bodies allowed? | No (signatures only) | Yes (mix of concrete + abstract) |
| Fields allowed? | No | Yes |
| Constructors? | No | Yes (called by subclasses) |
| Multiple inheritance? | A class can implement many interfaces | A class can extend only one abstract class |
| When to choose | Pure contract; many unrelated classes implement it | Shared base behaviour with hooks for subclasses |
Rule of thumb: start with an interface. Move to an abstract class only when you find yourself duplicating the same boilerplate across every implementation.
Interfaces and testing
The biggest practical win of interfaces is test mocking. If your service class depends on a real callout class, you can’t easily test it without hitting the network. If it depends on an interface, you swap in a fake implementation for tests.
public interface StripeGateway {
String charge(Decimal amount, String token);
}
public class StripeGatewayImpl implements StripeGateway {
public String charge(Decimal amount, String token) {
// real HTTP callout
return 'ch_real_...';
}
}
@isTest
public class StripeGatewayMock implements StripeGateway {
public String charge(Decimal amount, String token) {
return 'ch_test_mock';
}
}
public class OrderProcessor {
@TestVisible private StripeGateway gateway = new StripeGatewayImpl();
public void process(Order o) {
String chargeId = gateway.charge(o.Total__c, o.Payment_Token__c);
}
}
In the test, you set OrderProcessor.gateway = new StripeGatewayMock() and the production code runs unchanged but against a fake gateway. This pattern — sometimes called the Stub API — is how mature Apex codebases isolate external dependencies.
What interviewers are really looking for
The dictionary answer is “a list of unimplemented methods.” The senior answer hits three points: (1) interfaces enable polymorphism without inheritance, (2) the platform itself uses them everywhere (Batchable, Queueable, Schedulable), (3) they’re the cleanest way to mock dependencies in tests. Mention System.Callable as the generic “anything can be called” interface, and you’ve shown architecture-level awareness.
Verified against: Apex Developer Guide — Interfaces. Last reviewed 2026-05-17 for Spring ‘26 release.