Skip to main content
Group

Generics in Apex

Welcome! This is a group for the Developer community to share feedback and use cases on implementing Generics in Apex. It's here to keep an open channel for discussion about the ongoing development of the feature.

Resources from the Salesforce Developers Ask Me Anything on Apex Updates + Innovations

 

I hope you joined us for the fantastic SFDevsAMA on Wednesday 29th November featuring @Mohith Shrivastava and @Daniel Ballinger. They loved answering your questions about Apex. 

 

If you missed it, don't worry - we recorded the series and our experts have provided some great resources that our experts for you to continue your learning! 

 

Still have questions? Post them in this thread for our experts!Resources from the Salesforce Developers Ask Me Anything on Apex Updates + Innovations I hope you joined us for the fantastic SFDevsAMA on Wednesday 29th November featuring and .

@DataWeave in Apex @Salesforce Apex Hours @Apex Transaction Finalizers @Generics in Apex @User-Mode Database Operations

 

#Ask An Expert #CommUpdates #Apex #Apex Class #ApexDevelopment #Apexhours

12 comments
  1. Jul 5, 2025, 3:03 PM

    Using libraries like ApexMocks is now super verbose because of the lack of generics. 

    With generics, this:

    private static final MyService myService = (MyService) apexMocks.mock(MyService.class);

    could be written as this:

    private static final MyService myService = apexMocks.mock(MyService.class);

     

    And mock verification would change from: 

     

    ((MyService) apexMocks.verify(myService)).doSomething();

    to

    apexMocks.verify(myService).doSomething();
0/9000

It would be really useful to support covariant return types alongside this. That way, we can have generics in a class hierarchy where fluent methods can return the correct types. 

 

For example, consider an example in Java where we want to define a custom list type with a generic type T (MyList<T> in the example).  There may well be things that we want to do differently when we know that we have a list of SObjects (for example, have a method to do a database update). 

 

In the example below, the fluent method, add(), returns "this". SObjectList can override add() to return itself rather than a MyList, keeping the right context as we call add() a number of times. 

 

That pattern is not currently possible in Apex because it does not support covariant return types (https://ideas.salesforce.com/s/idea/a0B8W00000GdXOCUA3/support-covariant-return-types-in-apex). 

 

public class Main {

public static void main(String[] args) {

MyList<Integer> intList = new MyList<Integer>()

.add(3)

.add(5);

System.out.println("ints: " + intList.toString());

SObjectList<Account> accountList = new SObjectList<Account>()

.add(new Account("Foo"))

.add(new Account("Bar"))

.doUpdate();

System.out.println("SObjects: " + accountList.toString());

}

static class SObject {

@Override

public String toString() {

return "Hi, I'm an SObject";

}

}

static class Account extends SObject {

private final String name;

public Account(String name) {

this.name = name;

}

@Override

public String toString() {

return "Account Name: " + name;

}

}

static class MyList<T> {

private final List<T> theList;

public MyList() {

theList = new ArrayList<T>();

}

public MyList<T> add(T toAdd) {

theList.add(toAdd);

return this;

}

public String toString() {

return theList.stream().map(Objects::toString).collect(Collectors.joining(", "));

}

}

static class SObjectList<T extends SObject> extends MyList<T> {

public SObjectList() {

super();

}

@Override

public SObjectList<T> add(T toAdd) {

super.add(toAdd);

return this;

}

public SObjectList<T> doUpdate() {

System.out.println("Do a database update!");

return this;

}

}

}

0/9000

I come from a heavy Java background; I like my optionals.

 

Quick thoughts on why to use Optionals

 

1. Avoiding Null Pointer Exceptions: Optionals provide a way to avoid null references and null pointer exceptions, which are a common source of bugs and crashes in software. 

 

2. Improved Code Readability: When you see an Optional in a method's return type or as a parameter, it signals that the value might not always be there, making the code's behavior more apparent.

 

3. Encouraging Explicit Handling: When working with Optionals, developers are encouraged to explicitly handle both the presence and absence of a value, leading to more robust and predictable code.

 

public class MyOptional<T> {

private T value;

public MyOptional(T value) {

this.value = value;

}

public Boolean isPresent() {

return value != null;

}

public T get() {

return value;

}

}

We could use MyOptional(sObjectType/sObject) instead , but the strong casting could be verbose and error prone.

 

Perhaps I am looking at things wrong, curious to hear everyone's opinion :) 

 

Thanks

1 answer
  1. Sep 13, 2023, 7:24 PM

    Great post!

     

    I think the best way to use Optionals in our environment, if at all, will depend on the specific use case. Hard to find a silver-bullet... async over-usage is an example 

     

    (I know you, personally, have experienced folks that use async as the default, where synchronous is an exception.  We know this mindset does have its own treasure trove of associated issues.) 

     

    However, I think that they are a valuable tool that can help to improve the quality of code.

     

    I guess what I'm saying is that your level of familiarity with the toolset provided is invaluable for the engineer-mindset of finding the best solution rather than the most familiar to the developer.

0/9000

Here's a use-case I recently came across for generics. I am writing JSON deserializer classes for a REST API with multiple resources. Each resource also has a paginated response.

 

For example, it has a User response:

public class User {

public String id;

public String userName;

}

And a PaginatedUsers response:

public class PaginatedUsers {

public String nextPage;

public Integer pageNumber;

public User[] results;

}

Without generics, I need to write a deserializer for every paginated response for every resource (I can use inheritance to remove some of the boilerplate, but that actually increases my number of classes by one.) With generics, I could write a single paginated deserializer for all resources:

public class PaginatedResource<T> {

public String nextPage;

public Integer pageNumber;

public T[] results;

}

Then deserialize like this:

PaginatedResource<User> paginatedUserResponse = (PaginatedResource<User>)JSON.deserialize(

res.getBody(),

PaginatedResource<User>.class // <-- Will this be possible?

);

3 answers
  1. Aug 29, 2023, 12:59 PM

    @Andres Perez (ELTORO.it) In this example, User isn't an SObject. It's a User table from the external service. The idea is that the response would be deserialized and processed, so upcasting to Object wouldn't satisfy the use case either. Here's what the rest of the processing logic might look like

     

    PaginatedResource<ExternalResourceUser> paginatedUserResponse = (PaginatedResource<ExternalResourceUser>)JSON.deserialize(

    res.getBody(),

    PaginatedResource<ExternalResourceUser>.class

    );

    Contact[] contacts = new List<Contact>();

    for (ExternalResourceUser u : paginatedUserResponse.results) {

    contacts.add(

    // v--- The response body needs to be deserialized so that these attributes exist

    FirstName = u.givenName,

    LastName = u.surname,

    Email = u.emailAddress

    )

    }

0/9000

Expanded options for common generic collections

 

Across a number of companies and orgs, I routinely see people using what are essentially multimaps with a Map<Object, List<Object>> construction because the support for generics hasn't made available, so it's just as fast to write the logic every time as making a new class every time.

 

With generics, we'll be able to replace this:

Map<Id, List<SomeObject>> mapOfLists = new Map<Id, List<SomeObject>>();

// Add/put value

Id key = '00000000000AAAA';

SomeObject newValue = getSomeObject();

List<SomeObject> values = mapOfLists.get(key);

if (values == null) {

values = new List<SomeObject>();

values.add(newValue);

mapOfLists.put(key, values);

} else {

values.add(newValue);

}

// Iterate over stored list

List<SomeObject> values = mapOfLists.get(key);

if (values != null) {

for (SomeObject obj : values) {

// Do something

}

}

With this:

Multimap<Id, SomeObject> multimap = new Multimap<Id, SomeObject>();

// Add/put value:

Id key = '00000000000AAAA';

SomeObject newValue = getSomeObject();

multimap.put(key, newValue);

// Iterate over stored list

for (SomeObject obj : multimap.get(key)) {

// Do something

}

Where previously, making the above generic would only work with a specific type of object. Defining Multimap as a "Map<Id, List<SObject>>" is common, but not as dynamic as a generic such as the above that may not use Id as the key, or an SObject as the value.

 

Generics would also allow users to make their own implementations (or wrappers) of existing classes, such as to add a "getOrDefault" or "putIfAbsent" methods to their Map implementation, without needing to reference utility methods.

0/9000

With generics should come better for-loop support

It's unclear why the enhanced for-loop in Apex is restricted to only iterating over a Set or List, but perhaps the very narrow support for type parameters had something to do with it?  With more complete support for generics, Salesforce should consider broadening the types that for-loops can iterate over, as captured in this Idea:

https://ideas.salesforce.com/s/idea/a0B8W00000GdYeuUAF/allow-iterable-for-loops-in-apex-code

 

Just posting this to hopefully plant some seeds or provide a gentle nudge!

2 comments
0/9000

Here's a use case that I'd like addressed by generics: Removing the redundant casting in a test data factory. Here's an example in today's world:

 

Account testAccount = (Account)testRecordSource.getRecord(Account.SObjectType).withInsert();

And here's how it might look with generic methods:

 

Account testAccount = testRecordSource.getRecord<Account>().withInsert();

Now, this would require the implementation of getRecord() to have access to the fully qualified name of the type it is parameterised with. The data factory here is using the SObjectType to find a CMDT record which tells it how to make a valid Account in this org. 

 

More generally it would be great to have access to the Type at runtime within the generic method. That way, you'd be able to do things like call newInstance().

 

To take it even further, it would be cool to have Type.newInstance() also be generic. That way I could write a generic method like this:

 

public T myMethod<T extends SObject>() {

T newInstance = T.newInstance<T>();

// Do some things with newIstance

return newInstance;

}

Some of this might be a bit a bit fuzzy in my use of generics since I've been in Apex so long I've somewhat forgotten Java, C++, and C#

0/9000

First of all thank you for opening up this group and enabling the community to have a voice in this. I know it's a language feature many have long wanted!

 

I would really like to see type constraints when this is implemented. We have methods that accept either sObjects or collections of sObjects when we don't really mean any sObject. We really mean two or three specific sObject types. Or we might have methods that currently accept an instance of a class that implements an interface. But we might actually want to know what the class is (for example to return the same type instead of the interface).

 

I would imagine this might look something like

 

// Method that takes a list of sObjects that are all either obj1__c or obj2__c

public void mySObjectMethod(List<sObject:obj1__c|obj2__c> objects) {

}

// Method that takes a single object parameter that is of a class that implements interface MyInterface and returns an object of the same class

public T myObjectMethod(<T:MyInterface> arg) {

}

Finally we might want to apply this at a class level (in fact this may be the primary use case

// Parameterised class with a constraint that the type implements MyInterface

public with sharing MyClass<T:MyInterface> {

public T myMethod(T arg) {

}

}

4 comments
0/9000

It would also be awesome if we were able to use Covariance with Generics, for example, declare a type based on interface and use based on implementation:

MyList<SomeInterface> myListExample = new MyList<SomeInterface>();

SomeImplementation myImplementation = new SomeImplementation();

// This currently fails to compile

myListExample.add(myImplementation);

2 answers
  1. Mar 21, 2023, 4:24 PM

    I may have mixed, and the List class is not the best example.

    I want to create a class that receives a type T which I can set as Fruit and then add Apple and Banana instances.

    Again, my expectations are towards .NET implementation but without its quirks.

0/9000

Another desired generics example

Domain framework to support/promote bulkified code -- SObjects parent class contains logic reusable across multiple SObject types, while a domain subclass would be define for each SObject type where SObject-specific logic is needed.  

 

Today:

public virtual inherited sharing class SObjects {

private final List<SObject> records;

private SObjects() {

this.records = new List<SObject>();

}

public SObjects(List<SObject> records) {

this.records = records;

}

// Creates a new SObjects instance.

// Subclasses should override this method and create an appropriate subtype.

protected virtual SObjects newInstance(List<SObject> records) {

return new SObjects(records);

}

public List<SObject> getRecords() {

return records;

}

public SObjects filterFieldChanged(Map<Id,SObject> oldMap, Set<SObjectField> fields) {

List<SObject> filtered = new List<SObject>();

// ... filter logic ...

return newInstance(filtered);

}

public Map<Id,SObject> mapByIdToSObject() {

return new Map<Id,SObject>(records);

}

protected Map<Id,SObjects> mapByRecordTypeIdToSObjects() {

Map<Id,SObjects> result = new Map<Id,SObjects>();

for (SObject rec : records) {

Id key = (Id) rec.get('RecordTypeId');

if (key != null) {

SObjects value = result.get(key);

if (value == null) {

value = newInstance(new List<SObject>());

result.put(key, value);

}

value.records.add(rec);

}

}

return result;

}

}

public inherited sharing class Accounts extends SObjects {

public Accounts(List<Account> records) {

super(records);

}

// must remember to override factory method for use by parent

protected override SObjects newInstance(List<SObject> records) {

return new Accounts(records);

}

// must redefine with new name to cast

public List<Account> asList() {

return getRecords();

}

// must redefine with new name to cast

public Map<Id,Account> mapById() {

return (Map<Id,Account>) mapByIdToSObject();

}

// must redefine with new name to cast

public Map<Id,Accounts> mapByRecordTypeId() {

return (Map<Id,Accounts>) mapByRecordTypeIdToSObjects();

}

}

With generics:

public virtual inherited sharing class SObjects<T,U> {

private final List<T> records;

public SObjects() {

this.records = new List<T>();

}

public SObjects(List<T> records) {

this.records = records;

}

protected virtual U newInstance(List<T> records) {

// can something like this be made possible / more elegant?

U result = U.newInstance();

result.setRecords(records); // TODO add method and make class variable non-final

return result;

}

public List<T> getRecords() {

return records;

}

public Map<Id,T> mapByIdToSObject() {

return new Map<Id,T>(records);

}

// use domain class as map value to encourage continued bulk operations

protected Map<Id,U> mapByRecordTypeIdToSObjects() {

Map<Id,U> result = new Map<Id,U>();

for (T rec : records) {

Id key = (Id) rec.get('RecordTypeId');

if (key != null) {

U value = result.get(key);

if (value == null) {

value = newInstance(new List<T>());

result.put(key, value);

}

value.records.add(rec);

}

}

return result;

}

// return same type to encourage continued bulkified operations

public U filterFieldChanged(Map<Id,T> oldMap, Set<SObjectField> fields) {

List<T> filtered = new List<T>();

// ... filter logic ...

return newInstance(filtered);

}

}

public inherited sharing class Accounts extends SObjects<Account, Accounts> {

public Accounts(List<Account> records) {

super(records);

}

// no need to override newInstance() in each subclass if parent can construct correct type

// getRecords() can be used from parent directly and returns type List<Account>

// mapByIdToSObject() can be used from parent directly and returns type Map<Id,Account>

// mapByRecordTypeIdToSObjects() can be used from parent directly and returns type Map<Id,Accounts>

// filterFieldChanged() can be used from parent directly and returns type Accounts

// ... other bulk methods specific to Account continue on ...

}

Love that Salesforce is tackling this and gathering real-world use cases!

1 comment
  1. Mar 14, 2023, 8:37 PM

    I'd like to think that

    U result = U.newInstance();

    could be as simple as

    U result = new U();

    But perhaps it can't.

     

    In general I think your example needs type constraints that I outlined in another comment. Otherwise how does the system know that this is legal:

    result.setRecords(records);

    With type constraints you'd declare an interface that defined the methods the various U's would implement and the have

    public virtual inherited sharing class SObjects<T,U : IMyInterface> {

    or maybe

    public virtual inherited sharing class SObjects<T,U> where U: IMyInterface {
0/9000