Read more at, or return to the homepage

Apex Object-Oriented Basics

I've received a number of requests asking for a little background on Apex, so I've decided to create this little tutorial from the ground up. We'll be covering a number of different topics, including:

But before we begin, let's back up a step further and ask the question: why is writing quality code important?

linkThe Importance of Code Quality (And Compassion)

Writing quality code is an exercise, just as being compassionate is something we should all work towards. Nobody starts off writing perfect code, nor should anybody expect that of themselves. We all write code for various reasons: either because we're paid to do so or we enjoy it, our end goal in writing code should not be to write code, but to accomplish things. Our client/company needs a feature. We need functionality. In the end, the "writing of it" is often separate from the "how it works," and that is to our detriment. Developers who think their only responsibility should be writing code frequently seem surprised when their code doesn't work as intended.

Salesforce has spent billions making Apex, Lightning Web Components, etc ... into the lowest possible programming denominator (with the possible exception of those other leveler-of-playing-fields: JavaScript and Python). We're not God's gift to mankind by virtue of our ability to write something in possibly the most well-documented languages on the planet. Rather, the real gift that we should foster and focus on is our ability to identify problems and solve them in a sensible fashion; to be able to pick up where we left off when returning to something, and to be able to help our users. There's a reason a whole industry has sprung up preaching to programmers the importance of clean code — it costs hundreds of billions of dollars a year for corporations to make changes to their codebases. The cost of programming hubris — from un-needed complexity, to undetected or difficult to track down bugs — goes undiagnosed, but is certifiably real. Trying to save money by spending a little bit more time upfront is almost always worth it. Similarly, practicing compassion towards the needs of others tends to foster the ability to be more compassionate with yourself.

linkObject Oriented Programming Basics

Apex is a statically typed, object-oriented language. What does that mean, practically? It means that all constructs that you create extend from an abstract type, Object - which has two methods on it, both of which can also be overridden by you (more on overriding later):

A programming language is statically typed if all objects within the code have a known type while the code is running. Putting the two together, we can create objects - or distinct instances of a type:

Anonymous Apex
1linkpublic class Foo {

2link // you now have created a template

3link //for objects corresponding to type Foo

4link //Foo is a child of the base Object class

5link //as we will see below



8link//All objects have types.

9link//You can get the type instance for an object

10link//by calling ".class" after its name, or through

11link//the method Type.forName('Foo')

12linkType fooType = Foo.class;


14linkSystem.debug(new Foo().hashCode());

Running those statements in an Anonymous Apex console window (yes, you can create classes within Anonymous Apex!) produces:

1link$USER_DEBUG [6]|DEBUG|70822

2link$USER_DEBUG [7]|DEBUG|1827246551

You'll notice that the hashCode for the instance of the Foo object is substantially smaller than that of an actual instance of Foo, which is produced through the use of the new keyword. In object-oriented languages, you create (or initialize, or instantiate) objects through the use of the keyword new — this "calls" (or invokes) the constructor of an object. If a constructor is not defined for an object, the object uses a default constructor to create itself.

Without a constructor, you lack a way to initialize values to sensible defaults:

1linkpublic class SpecialNumber {

2link //this object now has a property

3link //called Number

4link public Integer Number;



7link//produces a null-reference exception - ouch!

8linknew SpecialNumber().Number.format()

With a constructor, you can do things like set up sensible defaults for your object's properties:

1linkpublic class SpecialNumber {

2link //the constructor ALWAYS

3link //has the same name as the class

4link public SpecialNumber() {

5link //"this." is optional

6link //later we'll review why I prefer

7link //the use of "this" in classes

8link this.Num = 0;

9link }


11link public Integer Num;



14link//prints '0'

15linknew SpecialNumber().Number.format();

By default, all objects have a zero-argument constructor; once you have defined a constructor for your object, though, it no longer has a zero-argument constructor by default:

1linkpublic class SpecialNumber {

2link //you might even test

3link //that the number coming is null

4link //and fall-back to a sensible default

5link //but in general, we try to avoid complex logic

6link //within constructors

7link public SpecialNumber(Integer num) {

8link this.num = num;

9link }

10link public Integer Num;



13link//aw jeez, we just produced another error, this time for ...

14linkSpecialNumber somethingSpecial = new SpecialNumber();

15link//Constructor not defined: [SpecialNumber].<Constructor>()

But you can always bring back your zero-argument constructor — an object can have as many constructors as you'd like:

1linkpublic class SpecialNumber {

2link public SpecialNumber() {

3link this.num = 0;

4link }

5link public SpecialNumber(Integer num) {

6link this.num = num;

7link }

8link public Integer Num;



11link//this works just fine

12linkSpecialNumber somethingSpecial = new SpecialNumber();

In some languages, you can also define a destructor for your object, with defined logic for what to do when that object is garbage collected. Not here, though. Garbage collection is an extremely interesting (and fraught) topic which we won't cover in great detail. Suffice to say, there's a reason that Rust is gaining popularity in the programming world — when you initialize something, as we just did, when storing an instance of SpecialNumber in the variable somethingSpecial, Apex now needs to allocate memory for that object. It needs to know when it's safe to stop holding that object in memory. This is one of the reasons why Salesforce has heap size limits for Apex — it needs to verify that you don't exceed the memory available at a given time in your instance when performing computing. Clearing the available memory of unused objects? That's garbage collection. Let's move on.

linkThe Importance Of Encapsulation (Accessors: Public/Private)

One of the central tenets in object-oriented programming is that objects encapsulate their internals. What does that mean, practically? It means that the programmer is averse to letting users of their object — even if that user is the same person who wrote the code for the object in question — reference things that they shouldn't outside of it. In our SpecialNumber example, we've just committed the cardinal sin, therefore, of exposing the Num property. We've also failed to signal intent, by virtue of poor naming. Let's fix both of those things:

1linkpublic class EmployeeFacts {

2link public EmployeeFacts() {

3link this.DaysOffAllowance = 0;

4link }


6link public Integer DaysOffAllowance { get; private set; }


Now we're getting somewhere. By changing the name to something associated with our company/client's domain, we're making the code accessible not only to ourselves, in the moment of writing it, but to users and stakeholders years down the line. It doesn't take a genius to figure out, without knowing anything about programming, that this object is going to be used to store data about employees. Communicating intent through the use of good names is one of the hardest things we can do when writing code.

In truth, defining a property on a class by simply giving it a type and name is equivalent to this syntax:

1linkpublic Integer DaysOffAllowance { get; set; }

The "get/set" syntax defines how the property is accessed. The "public/private" keywords denote who can access. The "private set" example now makes it impossible for other consumers of the EmployeeFacts object to update the stored value for the DaysOffAllowance property; a "private" accessor says "no!" to being referenced outside of the object it's within. When something is public, it can be seen and accesssed by any other class or code (in the same namespace, but more on that later).

Getters and setters, as they're known, are very versatile manipulators of object members (we sometimes refer to properties on an object as its "members", or "member variables", or "instance variables"). But in order to proceed further, we need to introduce methods, or functions. Let's look at the function syntax for getters/setters, using what's known as a "backing variable", and the fairly dense syntax:

1link//in EmployeeFacts

2link//the "_p" stands for private

3link//this used to be fairly commonplace in Java codebases

4link//in C#, variables can start off with an underscore

5link//and you see a lot of _privateVariableNaming

6linkprivate Boolean hasDaysOff() {

7link if(DaysOffAllowance_p < 5) {

8link return false;

9link }

10link return true;



13linkprivate Integer DaysOffAllowance_p;

14linkpublic Integer DaysOffAllowance {

15link get {

16link if(hasDaysOff()) {

17link return DaysOffAllowance_p;

18link } else {

19link return 0;

20link }

21link }

22link set {

23link DaysOffAllowance_p = value;

24link }


There's a lot going on here — and the syntax is fairly awful to look at. Methods either return a type, using the keyword return, or they are defined as void — they return nothing. You can "capture" what's returned from a method by setting a variable of the same type (or a type further up the object chain, more on that in a bit) equal to it. Thus, these two things are functionally equivalent to one another:

1linkprivate Boolean hasDaysOff() {

2link if(DaysOffAllowance_p < 5) {

3link return false;

4link }

5link return true;



8linkpublic Integer DaysOffAllowance {

9link get {

10link //this time we'll store a reference

11link //to the variable and THEN use it

12link Boolean hasDaysOff = this.hasDaysOff();

13link if(hasDaysOff) {

14link return DaysOffAllowance_p;

15link } else {

16link return 0;

17link }

18link }

19link set {

20link DaysOffAllowance_p = value;

21link }


You don't have to store the return value for a function — you do have to avoid setting something equal to a void method. The compiler won't let you do this:

1linkprivate void doThings() {

2link //do stuff here



5linkprivate Boolean otherMethod() {

6link //this won't compile!

7link Boolean someValue = this.doThings();


Something to take note of is the "value" referenced in the "set" method: value is a special keyword that in this case means "whatever's being passed to DaysOffAllowance."

This is one of the few instances where Apex is assuredly more powerful than its Java-underpinnings, as we can dispense with the backing variable entirely:

1linkprivate Boolean hasDaysOff() {

2link if(DaysOffAllowance < 5) {

3link return false;

4link }

5link return true;



8linkpublic Integer DaysOffAllowance {

9link get {

10link if(hasDaysOff()) {

11link //here you can refer to the variable name itself

12link return DaysOffAllowance;

13link } else {

14link return 0;

15link }

16link }

17link set;


We can also introduce the ternary expression, which allows you to use conditional logic to return different things. The ternary syntax is defined by an expression like such conditional that evalates to true or false ? value if true : value if false:

1linkprivate Boolean hasDaysOff() {

2link return DaysOffAllowance < 5 ? false : true;



5linkpublic Integer DaysOffAllowance {

6link get {

7link return hasDaysOff() ? DaysOffAllowance : 0;

8link }

9link set;


That's a lot better. Even if you knew nothing about programming, if you knew that the company you were working for didn't let you take days off until you had 5 days saved up, you could look at this code and glean some of its meaning. I used this particular example to introduce the concepts of:


In other programming languages, code is typically bundled into separate folders and namespaced — if code is in the same namespace, objects are unique and can't share the same name; if they're in different namespaces, two objects can share the same name. In some languages (like JavaScript) the namespace is really the structure of all the folders, and use of files elsewhere in the system relies on them being imported via relative filepath. In something like C#, the namespace is declared at the top of the class declaration:

1link//in C#, you don't have the full

2link//library available to you

3link//rather, you import references by namespace

4link//when you need to make use of different library


6linkusing System;

7linkusing System.Collections.Generic;


9linknamespace SalesforceObjects {

10link public class SalesforceAccount {

11link public string Name { get; set; }

12link public List<Datetime> SomeOtherProperties { get; set; }

13link }



With Apex, all of your code is either stored in src/classes/ or src/triggers/ and no namespacing is available to you. On the one hand, it's nice that you have access to the entire library's list of standard functions and classes without having to import them; on the other, it would be extremely nice to have the use of more folders to organize your code into different business domains, class types, etc ...

linkUse Of The "This" Keyword

When you see a reference to this in Apex code, it represents the current object in its entirety. Within an object, I prefer to reference all methods that are non-static (more on that in a second) with the use of the keyword this so that I know, 100% for sure, which methods are part of the object's class declaration.

Take this example, a slight branching from what I typically give with people when talking about the use of the Factory Pattern in Apex:

1linkpublic class Handler {

2link public Handler(Factory factory) {

3link //install dependencies needed in the handle method

4link }


6link public void handle(List<SObject> records) {

7link //do something with the records

8link }



11link//more on abstract classes in a moment!

12linkpublic abstract class Factory {

13link public static Handler getSObjectHandler() {

14link return new Handler(this);

15link }


Painting broad brush-strokes here: because the Factory passes itself through the use of this, the Handler class can make use of all the Factory's public methods when setting itself up.

linkInheritance (Class and Method Decorations)

OK, so we've established that all classes in Apex descend from the base Object, and that they inherit two methods from Object: equals and hashCode. When a class inherits from another class, it is said to extend that class. The basic Object inheritance is special, in that it does not need to be declared, but we can observe the effects of that inheritance within Apex easily:

1link//when you don't know

2link//the type of something

3link//object is ALWAYS allowed

4link//because everything descends from it

5linkAccount acc = new Account();

6linkObject accountObject = acc;


8link//outputs "true" - after all, "accountObject"

9link//and "acc" have the same reference

10link//you can also call acc.hashCode() to verify it

11link//has access to the Object class's methods

12linkSystem.debug(accountObject instanceof Object);

13link//outputs "true", of course!

Inheritance as a concept is an extremely important part of the DRY (don't repeat yourself) toolbelt that developers wield. By defining common sets of behaviors for your objects, you consolidate similar operations into statically typed groups of objects. I've spoken about how this kind of consolidation occurs in prior posts, but to be clear: refactoring, or the process of rewriting code to bundle commonalities while implementing new features, is the best time to find these commonalities and increase code re-use/reduce code repetition through the use of inheritance. Refactoring itself is made safe by having broad test coverage for your business logic — in itself, a topic for another day, but if you're interested in learning more about how crucial well-written tests will feature in your ability to quickly identify commonalities and make changes without fear of introducing bugs, I would encourage you to explore the other posts in The Joys Of Apex.

linkInheritance Basics: Casting Objects To A Specific Type

So, every object in the system descends from Object and can be cast to Object as a result. When we cast a variable in Apex, there are two directions that casting can go:

So, for example:

1linkAccount acc = new Account();

2linkSObject castAccount = acc; //works

3link//much later on in your code ...

4link//we'll use a method to demonstrate

5link//that if you only have access to the SObject

6link//but you safely know the type, you can downcast

7linkprivate void updateAccountName(SObject castAccount) {

8link Account acc = (Account)castAccount;

9link //now we can safely accesss acc.Name for setting

10link acc.Name = 'some new name';

11link //we could also have done this WITHOUT casting

12link //through the use of the .put method available on SObjects

13link //like castAccount.put(Account.Name, 'some new name');


Downcasting is particularly prevalent in Apex due to the lack of generics in the language. With generics, you can safely define helper methods that help you iterate through lists and other types of collections prior to returning the exact type you'd like. If that sentence didn't make sense, that's OK — I'll show you an example of why downcasting might be useful by demonstrating a helper method that pretty much everybody should be using:

1link//I keep this in a class called CollectionUtils

2linkpublic static Map<Id, SObject> convertToMap(List<SObject> sObjectList, SObjectField fieldName) {

3link Map<Id,SObject> mapping = new Map<Id,SObject>();

4link for(SObject sObj : sObjectList) {

5link if(sObj.get(fieldName) == null) continue;

6link //some people like to use validation that the SObjectField

7link //that's been passed in can actually be cast to an Id field

8link //that's a good idea, but this is a simple example

9link mapping.put((Id)sObj.get(fieldName), sObj);

10link //here we had to cast to Id because the method

11link //SObject.get always returns an Object!

12link }

13link return mapping;


The reason methods like this are so appealing within Apex is because the compiler knows, via inheritance, that a List<Account> is also a List<SObject>. Many times in Apex we receive Lists back from SOQL queries that are strongly-typed as a specific Salesforce object, and we need to relate instances of that object to another SObject by way of a lookup field. Creating a map where the key is the lookup field in question is crucial to your success in being able to perform complicated logic between two different types of SObjects.

Indeed, I would advise going further than this helper method and also having one that creates Maps keyed by String or Id to Lists of SObjects, crucial when processing data with a one-to-many relationship

linkSo What Exactly Is An SObject?

All programming languages come with a standard library. Apex is no exception, adding onto Java's standard library (sometimes in spectacularly helpful ways, like within the String Class). Some libraries leave their classes open to be extended (our next topic!); others allow you to use their objects but disallow inheritance. These closed off classes are known as sealed classes. You can imagine that somewhere in Salesforce's code repository lives something like the following declaration:

1link//global - like public, but even more accessible

2link//more on abstract in a second!

3linkglobal abstract sealed class SObject {

4link global Object get(SObjectField field) {

5link return this.get(field.getDescribe().getName());

6link }


8link global Object get(String fieldName) {

9link return this.sobject.get(fieldName);

10link }

11link //etc ...


That means we get the use of the SObject, which all Salesforce objects are extended from. Or perhaps it's an interface, which all objects inherit from. In either case, we can interact with the methods on the SObject class/type ... but we cannot inherit/extend them. Let's take a walk on the wild side of object inheritance:

linkMaking Objects Inherit: Interfaces, Abstract/Virtual Classes & The Protected Keyword

So how does one make a parent object which is later inherited from? There are two ways that inheritance can be accomplished within object-oriented programming:

Let's look at an example of an interface:

1link//while I'm not crazy about the "I"

2link//nomenclature for interfaces, because Apex lacks

3link//namespacing, it can be a useful way to distinguish

4link//interfaces from "concrete" objects

5linkpublic interface IPerson {

6link //interfaces don't declare visibility

7link //modifiers on their methods

8link String getName();



11linkpublic class ContactWrapper implements IPerson {

12link //more on the "final" keyword later

13link private final Contact con;

14link public ContactWrapper(Contact con) {

15link this.con = con;

16link }


18link public String getName() {

19link return this.con.FirstName + ' ' + this.con.LastName;

20link }



23linkpublic class LeadWrapper implements IPerson {

24link private final Lead lead;

25link public LeadWrapper(Lead lead) {

26link this.lead = lead;

27link }


29link public String getName() {

30link return this.lead.FirstName + ' ' + this.lead.LastName;

31link }



34linkLead lead = new Lead(FirstName = 'Test', LastName = 'Lead');

35linkContact con = new Contact(FirstName = 'Test', LastName = 'Contact');


37linkIPerson leadWrapper = new LeadWrapper(lead);

38linkIPerson conWrapper = new ContactWrapper(con);


40link//outputs "Test Lead"


42link//outputs "Test Contact"


Notice the use of the implements keyword, and that methods defined in an interface must have at least public visibility. (I say at least because technically there is a visibility above public, global, which means that a class can be accessed from any namespace / external code. We won't get into global here, though).

Notice, also, that though we know the child types of the Contact/LeadWrappers, we can safely set them to IPerson instances because they both inherit from the interface.

An example like this dovetails nicely into talking about abstract classes, since the implementation of the getName() method on each of the wrappers is essentially the same. Let's look at what this would look like making use of an abstract class. For now, we'll keep the interface (just to show you that you can):

1linkpublic abstract class Person implements IPerson {

2link private final SObject person;

3link //notice the use of the protected keyword

4link //this means that only classes inheriting from

5link //this class have the use of this constructor

6link //you can also make methods protected

7link protected Person(SObject person) {

8link this.person = person;

9link }


11link public String getName() {

12link return String.valueOf(

13link this.person.get('FirstName') +

14link ' ' +

15link this.person.get('LastName')

16link );

17link }


19link //abstract methods don't declare bodies

20link //their implementation must be defined in all

21link //classes extending from this one

22link public abstract String getUniqueIdentifer();


24link //more on static methods in a second

25link public static Person make(Lead lead) {

26link return new LeadPerson(lead);

27link }


29link public static Person make(Contact con) {

30link return new ContactPerson(con);

31link }


33link //classes cannot be protected

34link private virtual class ContactPerson extends Person {

35link protected ContactPerson(SObject contact) {

36link //calling "super" means "call the

37link //class I am derived from"

38link super(contact);

39link }


41link public override String getUniqueIdentifier() {

42link //this could be some specific set of business logic

43link //unique to all of your org's contacts

44link //I'm putting something nonsensical here

45link //purely to demonstrate how these methods are defined

46link return String.valueOf(this.hashCode());

47link }

48link }


50link private virtual class LeadPerson extends Person {

51link protected LeadPerson(SObject lead) {

52link super(lead);

53link }

54link public override String getUniqueIdentifier() {

55link //again, just an example

56link return String.valueOf(this.hashCode() + 'Lead');

57link }

58link }

59link }

There's a lot to discuss here:

1link//in Person.cls

2link//calling make is done like such:

3linkContact con;

4linkIPerson person = make(con);


6link//outside of Person.cls

7linkContact con;

8linkIPerson person = Person.make(con);


10link//using "this" with a static method won't compile:

11link//in Person.cls

12linkContact con;

13linkthis.make(con); // won't compile

Specifically, static methods cannot be used in conjunction with the new keyword; because they are not tied to a specific instance of an object, they cannot reference member variables or non-static methods.

linkA Prelude to Polymorphism

Your code will benefit tremendously in terms of organization and lack of repetition through the use of inheritance. Objects typically arise out of your own ability to recognize patterns between disparate areas in your codebase. I've worked for some orgs where there were extremely long inheritance chains between objects, and orgs that kept it simple with only a few objects extending/inheriting from one another. Different strokes, as they say.

What does a longer object chain look like in Apex? Let's find out by going back to IPerson:

1linkpublic interface IPerson {

2link String getName();



5link//interfaces can extend from other interfaces(!)

6linkpublic interface IDealBreaker extends IPerson {

7link String getDealBreakerReason();



10link//in Person.cls

11linkpublic static DealBreakerPerson make(Lead lead, String dealBreakerReason) {

12link return new DealBreakerPerson(lead, dealBreakerReason);



15link//public since the "make" method references it

16link//could still be private if the "make" method instead returned IDealBreaker

17link//"extends" ALWAYS comes before "implements"

18linkpublic virtual class DealBreakerPerson

19link extends LeadPerson

20link implements IDealBreaker {

21link private final String dealBreakerReason;

22link protected DealBreakerPerson(Lead lead, String dealBreakerReason) {

23link super(lead);

24link this.dealBreakerReason = dealBreakerReason;

25link }


27link public String getDealBreakerReason() {

28link return this.dealBreakerReason;

29link }


Let's test(!) that everything works as expected, first with some anonymous apex, then in an actual test class (our first example test class!):

1linkIPerson lead = Person.make(new Lead());

2linkIDealBreakerPerson leadWithDealBreakerReason =

3link Person.make(new Lead(), 'Contract too expensive');


5link//works, because every instance of IDealBreakerPerson

6link//is also an instance of IPerson


8link//doesn't work, LeadPerson doesn't implement getDealBreakerReason





13link//and then in actual tests:


15linkprivate class PersonTests {

16link static Lead lead = new Lead(FirstName = 'Test', LastName = 'Person');


18link @isTest

19link static void it_should_return_name_for_lead_person() {

20link IPerson person = Person.make(lead);


22link System.assertEquals('Test Person', person.getName());

23link }


25link @isTest

26link static void it_should_return_name_for_contact_person() {

27link IPerson person = Person.make(

28link new Contact(FirstName = 'Test', LastName = 'Contact')

29link );


31link System.assertEquals('Test Contact', person.getName());

32link }


34link @isTest

35link static void it_should_return_name_and_dealbreaker_reason_for_dealbreaker_lead() {

36link String dealBreakerReason = 'Contract too expensive';

37link IDealBreaker dealbreakerLead = Person.make(lead, dealBreakerReason);


39link System.assertEquals('Test Person', dealbreakerLead.getName());

40link System.assertEquals(dealBreakerReason, dealbreakerLead.getDealBreakerReason());

41link }


Running the tests produces the following:

1link$[pass] PersonTests: it_should_return_name_and_dealbreaker_reason_for_dealbreaker_lead, time: 0.027s

2link$[pass] PersonTests: it_should_return_name_for_contact_person, time: 0.005s

3link$[pass] PersonTests: it_should_return_name_for_lead_person, time: 0.006s

Yum. Who doesn't love a nice passing test? But let's take it a step further. Let's say that management wants to receive a special alert for Leads who fall out with certain keywords left by Sales in the Lead's description field

This is a stretch, for sure, but I'm trying to keep things simple for the purpose of these examples. You could actually imagine something like this taking place, with slightly different bounds; for example, wanting automation if a task was logged against a closed lead containing certain words

1linkpublic interface ISpecialDealBreaker extends IDealBreaker {

2link Boolean isVIPLead();



5link//in Person.cls

6linkpublic static ISpecialDealBreaker makeSpecial(Lead lead, String dealBreakerReason) {

7link return new SpecialDealBreakerPerson(lead, dealBreakerReason);



10link//updating the visibility of the stored

11link//person to protected so that SpecialDealBreakerPerson

12link//has access to it

13linkprotected final SObject person;

14linkprotected Person(SObject person) {

15link this.person = person;



18link//class declarations can get a little

19link//verbose with inheritance, it's true,

20link//but the benefits you gain more than make up for it!

21linkpublic virtual class SpecialDealBreakerPerson

22link extends DealBreakerPerson

23link implements ISpecialDealBreaker {

24link protected SpecialDealBreakerPerson(Lead lead, String dealBreakerReason) {

25link super(lead, dealBreakerReason);

26link }


28link public Boolean isVIPLead() {

29link //use of the ternary to make this operation always safe

30link Lead lead = (Lead)this.person;

31link List<String> descriptionWords = lead.Description != null ?

32link lead.Description.split(' ') :

33link Boolean hasMatch = false;

34link for(String descriptionWord : descriptionWords) {

35link //typically, so-called "magic" strings that indicate something

36link //of business significance are declared as public static final Strings

37link //so that your test classes can use the same "magic" string in their setup

38link //avoid non-constant strings unless you have absolutely no other choice

39link if(descriptionWord.equalsIgnoreCase('SPECIALKEYWORD')) {

40link hasMatch = true;

41link //"break" is a special keyword in loops

42link //that exits the loop early

43link //this is a performance optimization -

44link //once we know a match exists

45link //we don't need to keep searching for

46link //other matches

47link break;

48link }

49link //you could keep going with this logic

50link //testing different keywords

51link }

52link return hasMatch;

53link }


And then in the tests, we should verify that getName is still callable by this now-grandchild instance of ISpecialDealBreaker, and that each method works as advertised:

1link//in PersonTests.cls


3linkstatic void it_should_return_name_dealbreaker_reason_and_vip_status() {

4link String dealBreakerReason = 'Some other reason';

5link ISpecialDealBreaker specialLead = Person.makeSpecial(lead, dealBreakerReason);


7link System.assertEquals('Test Person', specialLead.getName());

8link System.assertEquals(dealBreakerReason, specialLead.getDealBreakerReason());

9link System.assertEquals(false, specialLead.isVIPLead());


11link lead.Description = 'something something SPECIALKEYWORD something';

12link //this works because the REFERENCE to the lead

13link //is still the same in the wrapper!

14link System.assertEquals(true, specialLead.isVIPLead());


And the tests pass! We have access to the grandparent's methods from a grandchild instance.

I hope you can see from this (admittedly limited) example that inheritance and encapsulation can be combined to produce truly powerful results, putting business logic firmly into well-understood nomenclatures and keeping the messy parts contained (encapsulated) within objects.

One last note on the keyword virtual: when used with methods, a method can be both an override and virtual, as in public virtual override Boolean myMethodName() — this indicates that a method is both implementing a method from an abstract/virtual class "above" it in the hierarchy, while also allowing classes that inherit from it to also override the implementation defined here.

linkThe Power Of Polymorphism

So, we've learned about objects and how they can extend/inherit from one another with interfaces, abstract, and virtual classes. We've also learned about visibility and how to properly encapsulate the business logic within objects so that consumers of those objects get exactly what they need, and nothing more. We've learned about the importance of naming, and how to make code understandable to virtually anyone.

Perhaps you've heard a lot about polymorphism, and how you're supposed to use it to your advantage. To put it plainly, polymorphism means using the basic types of your objects instead of peering into their internals in order to figure out what to do.

First, here's an example of something that's procedural code making use of our Person object:

1link//in some class

2linkpublic void processPersons(List<Person> persons) {

3link for(Person person : persons) {

4link if(person instanceof Person.SpecialDealBreakerPerson) {

5link //don't process the VIPs

6link continue;

7link } else if(person instanceof Person.DealbreakerPerson) {

8link Person.DealBreakerPerson dealBreaker = (Person.DealBreakerPerson)person;

9link dealBreaker.markAsDealBrokenAndProcessed(;

10link } else if(person instanceof Person.LeadPerson) {

11link Person.LeadPerson leadPerson = (Person.LeadPerson)person;

12link leadPerson.markLeadAsProcessed(;

13link } else if(person instanceof Person.ContactPerson) {

14link Person.ContactPerson contactPerson = (Person.ContactPerson)person;

15link contactPerson.markAsProcessed(;

16link }

17link }


Without even seeing the internals of the methods like markAsDealBrokenAndProcessed and markLeadAsProcessed, you should be able to recognize that we're in a spot of trouble here. Imagine coming back to this code years down the line and trying to divine what was going on. You have some conditional logic that relies on the composition of the individual objects in a list, and depending on what type they are, you're going to do a whole host of things. Let's use the power of polymorphism to set this situation right:

1link//in Person.cls


3linkpublic virtual void process(Datetime processTime) {

4link //here we use a string instead of an SObjectField

5link //because we want both the Lead and Contact

6link //to make use of the same field!

7link this.person.put('ProcessTime__c', processTime);




11linkpublic virtual class DealBreakerPerson

12link extends LeadPerson

13link implements IDealBreaker {

14link private final String dealBreakerReason;

15link protected DealBreakerPerson(Lead lead, String dealBreakerReason) {

16link super(lead);

17link this.dealBreakerReason = dealBreakerReason;

18link }


20link //here we have to use virtual AND override so that

21link //SpecialDealBreakerPerson can bail out

22link public virtual override void process(Datetime processTime) {

23link //cool story - you can call "super"

24link //in virtual methods to opt-in to the parent's behavior

25link //prior to doing your own thing

26link super.process(processTime);

27link //assuming we have a custom field set up

28link //to track DealBreakers in particular

29link this.lead.DealBroken__c = true;

30link }

31link //...




35linkpublic virtual class SpecialDealBreakerPerson

36link extends DealBreakerPerson

37link implements ISpecialDealBreaker {

38link protected SpecialDealBreakerPerson(Lead lead, String dealBreakerReason) {

39link super(lead, dealBreakerReason);

40link }


42link //by leaving this method signature blank

43link //we are borrowing from the Null Object pattern

44link //look it up, it's worth it!

45link public override void process(Datetime processTime) {}

46link //...


Going back to our procedural code, it now looks fairly tame:

1linkpublic void processPersons(List<Person> persons) {

2link Datetime processedTime =;

3link for(Person person : persons) {

4link person.process(processedTime);

5link }


Boom! If you need to check this code years down the line, or (heaven forbid!) make changes to it, the business logic for processing people of different business specifications is now completely encapsulated within their respective classes. This minimizes the chance for bugs by reducing procedural code (if statements and case statements being where the vast majority of bugs that I have personally encountered derive from), and it also helps to properly categorize things.

linkThe Transient Keyword

You won't see transient used often, until you do. A property on an object can be marked as transient in order to avoid being serialized:

1linkpublic class DataTransferObject {

2link //will show up when serialized

3link public Person Person { get; set; }

4link //this property is hidden upon serialization

5link public transient Integer VersionNumber { get; set; }


Serialization, or the mechanism by which an object is turned into a String version of itself, is commonly seen when interacting with external APIs. It's also a way for you to get clever with Apex's insistence that @future methods only accept simple types, like Strings (for more on that, take a look at the post on Callouts / Callbacks that I wrote!!). If you don't work with external APIs often, or don't need to hide data from those APIs, there's a good chance you will never use the transient keyword!

linkWrapping Up

Well, we've really experienced it all together! We've gone from examining the origin of the "object" part of "object-oriented programming" to seeing firsthand how polymorphism can properly encapsulate business logic within classes. At this point, I'd love to recommend some additional resources for those of you beginning your Apex journey.

The three books I benefitted most from reading when I started out:

Perhaps some of you are surprised that Dan Appleman's book, Advanced Apex Programming doesn't make the list. Here's why — I think Advanced Apex Programming is great. It's held up remarkably well, even as Salesforce's platform has evolved. It doesn't teach the fundementals, though, or why the fundementals are important. For that, I think the classic texts associated with object-oriented programming cover crucial teaching ground!

Hopefully for those of you looking to get into Apex, or searching for a deeper understanding of concepts taught in a simple way, this proves to be a helpful resource for you. I'd like to thank you for taking the time to learn alongside me, and I hope that you'll check out other entries in The Joys Of Apex! If you'd like to persuse the code from this post, please feel free to explore further via my ApexMock's Github branch for object-oriented programming basics. Lastly, I had some "object-oriented nirvana" while designing the Custom Rollup solution looking to replace DLRS -- the subject matter in that post is much more advanced, but if you'd like to see some concrete examples of how OOP can help make your life easier, that post is a great place to start!

The original version of Apex Object-Oriented Basics can be read on my blog.

The Importance of Code Quality (And Compassion)Object Oriented Programming BasicsThe Importance Of Encapsulation (Accessors: Public/Private)NamespacingUse Of The "This" KeywordInheritance (Class and Method Decorations)Inheritance Basics: Casting Objects To A Specific TypeSo What Exactly Is An SObject?Making Objects Inherit: Interfaces, Abstract/Virtual Classes & The Protected KeywordA Prelude to PolymorphismThe Power Of PolymorphismThe Transient KeywordWrapping Up

Home Advanced Logging Using Nebula Logger Apex Logging Service Apex Object-Oriented Basics Batchable And Queueable Apex Building A Better Singleton Continuous Integration With SFDX Dependency Injection & Factory Pattern Enum Apex Class Gotchas Extendable Apis Formula Date Issues Future Methods, Callouts & Callbacks Idiomatic Salesforce Apex Introduction & Testing Philosophy Lazy Iterators Lightweight Trigger Handler LWC Composable Modal LWC Composable Pagination LWC Custom Lead Path Mocking DML React Versus Lightning Web Components Refactoring Tips & Tricks Replacing DLRS With Custom Rollup Repository Pattern setTimeout & Implementing Delays Sorting And Performance In Apex Test Driven Development Example Testing Custom Permissions The Tao Of Apex Writing Performant Apex Tests

Read more tech articles