Not exactly, but you can do something like lazy evaluation by making use of the Callable
interface (or rolling your own). Here's an example using Callable
...
To start with, a test:
@IsTest private class LazyTest {
@TestSetup static void setup() {
List<Account> accounts = new List<Account> {
new Account(Name = 'A'),
new Account(Name = 'B')};
insert accounts;
List<Contact> contacts = new List<Contact> {
new Contact(LastName = '1', AccountId = accounts[0].Id),
new Contact(LastName = '2', AccountId = accounts[1].Id)};
insert contacts;
}
@IsTest static void testBehavior() {
Callable lazyContacts = new LazyContacts(new LazyAccounts('A'));
// No calls made yet, so I can pass this around at no great cost
System.assertEquals(0, Limits.getQueries());
// OK, I've decided I need those contacts now...
List<Contact> results = (List<Contact>)lazyContacts.call(null, null);
System.assertEquals(2, Limits.getQueries());
System.assertEquals(1, results.size());
System.assertEquals('1', results[0].LastName);
}
}
In the test, lazyContacts
is not yet the Contacts I'm looking for. It's like a Callable
that will provide the Contacts when I actually need them. It's parameterised by another Callable
, so that inner one is also not invoked until you decide you actually need it.
The actual implementations:
public with sharing class LazyContacts implements Callable {
private Callable lazyAccounts;
public LazyContacts(Callable lazyAccounts) {
this.lazyAccounts = lazyAccounts;
}
public Object call(String param1, Map<String, Object> param2) {
return [
SELECT LastName
FROM Contact
WHERE AccountId IN :(List<SObject>)lazyAccounts.call(null, null)
];
}
}
public with sharing class LazyAccounts implements Callable {
private String accountName;
public LazyAccounts(String accountName) {
this.accountName = accountName;
}
public Object call(String param1, Map<String, Object> param2) {
return [
SELECT Id
FROM Account
WHERE Name = :accountName
];
}
}
You could certainly put together something like that in Apex. And you can do similar with iterators - only iterating when the data is actually requested.
The only downside is that Apex is missing a few language niceties that exist in Java and would make the code more succinct (and type-safe if we had templates).
?
null checks available in .Net. – sfdcfox Feb 25 '19 at 11:44