Classes & Objects
| | Static | Instance | |-|--------|----------| | Belongs to | The class | Each object | | Access | ClassName.member | obj.member | | Lifetime | One transaction (reset on every new request — not a persistent cache) | Until the object is unreferenced | | Typ…
11.1 Anatomy of a class
public with sharing class Invoice {
// ---- static members (shared by all instances, per transaction) ----
public static final Decimal DEFAULT_TAX = 0.18;
private static Integer instanceCount = 0;
// ---- instance fields ----
private String invoiceNumber; // note: 'number' is a reserved word in Apex
private List<LineItem> lines = new List<LineItem>();
// ---- properties ----
public String customerName { get; set; } // auto-property
public Decimal taxRate { get; private set; } // read-only outside the class
public Decimal subtotal { // computed property
get {
Decimal sum = 0;
for (LineItem li : lines) sum += li.total();
return sum;
}
}
// ---- constructors ----
public Invoice(String invoiceNumber) {
this(invoiceNumber, DEFAULT_TAX); // constructor chaining
}
public Invoice(String invoiceNumber, Decimal taxRate) {
this.invoiceNumber = invoiceNumber;
this.taxRate = taxRate;
instanceCount++;
}
// ---- instance methods ----
pu
…Invoice inv = new Invoice('INV-001')
.addLine('Widget', 3, 10)
.addLine('Gadget', 1, 25.50);
System.debug(inv.subtotal); // 55.50
System.debug(inv.grandTotal()); // 65.49
Invoice.LineItem li = new Invoice.LineItem('Thing', 2, 5);11.2 Static vs instance
| | Static | Instance | |-|--------|----------| | Belongs to | The class | Each object | | Access | ClassName.member | obj.member | | Lifetime | One transaction (reset on every new request — not a persistent cache) | Until the object is unreferenced | | Typical use | Utilities, constants, trigger recursion flags, caches within a transaction | State of a specific object |
Static variables are initialized once per transaction; a static initializer block can run setup code:
Instance initializer blocks ({ ... } without static) run before the constructor body.
public class CountryCodes {
public static final Map<String, String> BY_NAME;
static {
BY_NAME = new Map<String, String>{ 'India' => 'IN', 'Germany' => 'DE' };
}
}11.3 Constructors
If you write no constructor, a public no-argument constructor is provided.
Once you write any constructor, the default one disappears — add it explicitly if needed.
Overload constructors with different parameter lists; chain with this(...) (must be the first statement).
Call a superclass constructor with super(...) (§13).
11.4 Properties in depth
public class Temperature {
public Decimal celsius { get; set; }
public Decimal fahrenheit {
get { return celsius == null ? null : celsius * 9 / 5 + 32; }
set { celsius = (value - 32) * 5 / 9; } // 'value' is the implicit parameter
}
public Integer readings { get; private set; } // asymmetric access
public static String unit { get { return unit ?? 'C'; } set; } // static property
}11.5 Inner classes
Inner classes are declared inside an outer (top-level) class; only one level of nesting is allowed.
Inner classes are implicitly static in behavior: they cannot access the outer class's instance members.
They're ideal for wrapper/DTO types (Invoice.LineItem, AccountController.AccountWrapper), custom exceptions, and private helpers.
Default access for inner classes is private.
11.6 Object equality and hashing
To use your own class as a Set element or Map key, override equals and hashCode:
sObjects compare by field values, not by reference — changing a field of an sObject already in a Set or used as a Map key makes it unfindable. Use IDs as keys instead.
public class Point {
public Integer x, y;
public Point(Integer x, Integer y) { this.x = x; this.y = y; }
public Boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
}
public Integer hashCode() { return (31 * x) ^ y; }
}