Modern Apex Language Features

Collator-based locale-aware string sorting is also available (Collator.getInstance()).

38.1 Null-safe (?.) and null coalescing (??) operators

// Safe navigation (Winter '21)
String city = contact?.Account?.BillingCity;               // null if any link is null
Integer len = someString?.length();
String accName = [SELECT Name FROM Account WHERE Id = :someId]?.Name;     // null (no exception) if no row

// Null coalescing (Spring '24)
String displayCity = contact?.Account?.BillingCity ?? 'Unknown';
Integer qty = (Integer) input.get('qty') ?? 1;
Account acc = [SELECT Id FROM Account WHERE Name = 'Nope' LIMIT 1] ?? new Account(Name = 'Default');
// a single-row query that returns no rows yields the right-hand default instead of a QueryException

38.2 Features covered elsewhere

switch statements (Summer '18) — Integer, Long, String, enum, and sObject-type matching (§8.2).

The Assert class (Winter '23) — Assert.areEqual, isTrue, isInstanceOfType, fail (§22.4).

User mode database operations (GA Summer '23) — WITH USER_MODE, insert as user, AccessLevel.USER_MODE (§15.4, §17.3, §29.2).

38.3 Comparator interface (Winter '24)

Collator-based locale-aware string sorting is also available (Collator.getInstance()).

public class AccountByRevenue implements Comparator<Account> {
    public Integer compare(Account a, Account b) {
        Decimal x = a.AnnualRevenue ?? 0, y = b.AnnualRevenue ?? 0;
        return x == y ? 0 : (x > y ? -1 : 1);
    }
}
List<Account> accs = [SELECT Name, AnnualRevenue FROM Account LIMIT 100];
accs.sort(new AccountByRevenue());

38.4 DataWeave in Apex

Run DataWeave 2.0 scripts (MuleSoft's transformation language) from Apex for complex format conversion (CSV, JSON, XML). Scripts live in force-app/main/default/dw/ as .dwl files with a -meta.xml:

DataWeave execution counts toward CPU and heap limits; it shines for messy format transformations that would be verbose in Apex.

%dw 2.0
input records application/csv
output application/apex
---
records map (row) -> {
    FirstName: row.first_name,
    LastName: row.last_name,
    Email: row.email
} as Object {class: "Contact"}
DataWeave.Script script = new DataWeaveScriptResource.csvToContacts();
DataWeave.Result result = script.execute(new Map<String, Object>{ 'records' => csvString });
List<Contact> contacts = (List<Contact>) result.getValue();

38.5 Apex Cursors

Apex Cursors (Database.Cursor) let you traverse very large SOQL result sets in forward and backward chunks across multiple transactions (for example, inside chained Queueables) — a lighter alternative to Batch Apex:

Apex Cursors were introduced as a beta in Summer '24 (API 61.0) with their own limits (rows per cursor, fetch calls per transaction). Confirm GA status and current limits in the release notes for your org's version before using them in production.

public class ContactCursorJob implements Queueable {
    private Database.Cursor cursor;
    private Integer position;

    public ContactCursorJob() {
        cursor = Database.getCursor('SELECT Id, Email FROM Contact WHERE Email != null');
        position = 0;
    }
    public void execute(QueueableContext ctx) {
        List<Contact> scope = cursor.fetch(position, 200);
        position += scope.size();
        // ... process scope ...
        if (position < cursor.getNumRecords()) {
            System.enqueueJob(this);                 // continue with the same cursor
        }
    }
}

38.6 Other recent additions

UUID.randomUUID() (Spring '24) Generates version-4 UUIDs Database.queryWithBinds (Winter '23) Dynamic SOQL with a bind map

Queueable delay & AsyncOptions (Winter '24) Delayed enqueue, max stack depth, duplicate signatures Transaction Finalizers (Spring '21) Post-execution handling for Queueables

Security.stripInaccessible (Spring '20) FormulaEval / Formula.builder()

Evaluate formula expressions dynamically in Apex (introduced as beta/developer preview around 2024; check availability)

Detect the execution context (Queueable, Batch, Aura, REST, Anonymous, …)

Control serialization of Apex objects across namespaces Iterable for-each on Iterator types Custom iteration in loops Polymorphic SOQL TYPEOF GA in Apex for polymorphic lookups

🧪 Part III Exercises

Write a Queueable that calls an external API for each new Lead (mocked), with a Finalizer that logs failures to Log__c and retries up to three times.

Build a Batch Apex job that recalculates a Lifetime_Value__c field on Account from closed-won Opportunities, implementing Database.Stateful to report totals in finish, and schedule it nightly.

Create a platform event Invoice_Paid__e, publish it from a service class, and subscribe with a trigger that updates related records; test with Test.getEventBus().deliver().

Enable CDC on Contact and write a change-event trigger that logs which fields changed on updates.

Write a REST client class for a public JSON API using a Named Credential, typed DTOs, and error handling; test it with HttpCalloutMock for 200, 404, and 500 responses.

Expose an Apex REST resource /inventory/* with GET and POST methods, enforce user mode, and write tests by populating RestContext.

Build an LWC that searches Contacts with a wired cacheable method and creates a Contact imperatively, refreshing results with refreshApex.

Create an @InvocableMethod that accepts a list of Account IDs and returns a risk score for each; use it in a record-triggered Flow.

Refactor a trigger-heavy feature into Service, Selector, and Domain classes, with the selector injected so tests can use the Stub API.

Capture a debug log, step through it with the Apex Replay Debugger, then run Salesforce Code Analyzer on your project and fix every high-severity finding.