The private keyword in Apex restricts a member — a method, field, inner class, or property — to the class that declares it. Code in any other class, package, or trigger cannot see or call a private member. It’s also the default for any class member that doesn’t explicitly state an access modifier.
The rule
public class OrderService {
private Decimal taxRate = 0.18; // visible only inside OrderService
private Decimal calculateTax(Decimal amt) {
return amt * taxRate;
}
public Decimal totalWithTax(Decimal amt) {
return amt + calculateTax(amt); // OK — same class
}
}
Another class cannot do:
OrderService svc = new OrderService();
svc.calculateTax(100); // compile error: Method is not visible
svc.taxRate; // compile error: Variable is not visible
What private applies to
- Member methods — only invokable from the same class.
- Member fields — only readable/writable from the same class.
- Inner classes — only usable inside the outer class.
- Properties — getters and setters can each have their own modifier.
- Constructors — only callable from the same class (used for singleton patterns).
What private does NOT apply to
-
Top-level (outer) Apex classes. A top-level class is either
publicorglobal.privateon a top-level class is a compile error. Encapsulation at that level is achieved bypublic+ careful method/field visibility.// Not allowed: // private class OrderService {} // Use: public class OrderService { private Decimal taxRate; // visibility controlled inside }
Why default to private
Three reasons:
- Refactoring freedom. Code outside the class cannot have come to depend on a private method, so you can rename or restructure private methods without breaking callers.
- Smaller API surface. Less to test, less to document, less to maintain backward compatibility for.
- Clear public contract. When the only
publicmethods are the ones meant to be called, the class’s intended use is obvious at a glance.
The conventional approach in mature Apex codebases: private for everything by default, escalate to public only when something genuinely needs to be called from outside the class.
@TestVisible — the unit-test escape hatch
A private member can be marked @TestVisible to let test classes call it without changing the production access level:
public class OrderService {
@TestVisible
private Decimal calculateTax(Decimal amt) {
return amt * 0.18;
}
}
@isTest
class OrderServiceTest {
@isTest static void taxCalculation() {
OrderService svc = new OrderService();
Decimal tax = svc.calculateTax(100); // works only in test context
System.assertEquals(18, tax);
}
}
@TestVisible keeps the method private for everyone except @isTest classes — your encapsulation stays intact in production, but tests can probe internals.
private vs protected vs public vs global
| Modifier | Same class | Same package (managed) | Subscriber org code |
|---|---|---|---|
private | Yes | No | No |
protected | Yes | Yes (subclasses only) | No |
public | Yes | Yes | No (unless global) |
global | Yes | Yes | Yes |
protected is rarely used — it only matters inside an inheritance chain.
Common interview follow-ups
- Is
privatethe default? — Yes, for class members. Top-level classes default to nothing (must bepublicorglobal). - Can I mock a
privatemethod in a unit test? — Yes, with the Apex Stub API for@TestVisibleprivate methods, or by injecting an interface dependency. - Why does Apex make me write
privateif it’s the default? — Explicit access modifiers are clearer; many style guides require them. Most teams writeprivateeven where it’s redundant.
What interviewers are really looking for
The basic answer is “visible only within the class.” Strong signals: (1) default for members but not top-level classes, (2) the @TestVisible annotation as the right way to expose private to tests, (3) the convention to default to private and escalate to public only when external callers exist, (4) the difference between private (same class only) and protected (subclasses too).
Verified against: Apex Developer Guide — Access Modifiers. Last reviewed 2026-05-17.