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; }
}