6.1 Variables, Constants & Apex Data Types
Key Takeaways
- Apex is a strongly typed, Java-like language: every variable has a declared type, and the compiler rejects mismatched assignments
- Use final for constants; prefer Decimal over Double for money and precise currency math
- Primitive types include Integer, Long, Double, Decimal, Boolean, String, Date, Datetime, Time, Id, and Blob
- sObject types (Account, Contact, custom objects) are first-class values you can construct, query, and DML
- Uninitialized variables are null; null dereference throws NullPointerException—guard before calling methods on possibly null references
6.1 Variables, Constants & Apex Data Types
Quick Answer: Apex is a strongly typed, object-oriented language similar to Java. You declare variables with an explicit type, mark unchanging values with final, prefer Decimal for money, and treat null carefully because method calls on null throw NullPointerException. sObject types (standard and custom) are first-class values alongside primitives.
This section is foundation for Process Automation and Logic on Platform Developer I. Later chapters assume you can read type signatures, choose Decimal vs Double, and reason about null and conversion behavior under exam scenarios.
Declaring Variables and Constants
Declare a variable with a type, optional initializer, and a name that starts with a letter or underscore:
Integer quantity = 10;
String status = 'Open';
Boolean isActive;
Account acct = new Account(Name = 'Acme');
Uninitialized locals default to null (including numeric types). That is different from some languages that zero-initialize numbers. Writing Integer n; System.debug(n + 1); fails at runtime with a null-related error because you cannot unbox null for arithmetic without a non-null value.
Use final for values that must not be reassigned after initialization:
final Integer MAX_RETRIES = 3;
final String DEFAULT_STATUS = 'New';
final variables can be assigned once (including constructor or declaration). Attempting a second assignment does not compile. Class-level static final constants are common for limit thresholds, default statuses, and configuration keys that should not drift across methods.
Exam trap: final does not make an sObject’s field values immutable. You can still change acct.Name on a final Account acct reference; you simply cannot point acct at a different Account instance.
Primitive Types You Must Know
| Type | Role | Exam notes |
|---|---|---|
| Integer | 32-bit whole number | Overflow wraps; use for counts and small IDs |
| Long | 64-bit whole number | Larger ranges; suffix L in literals (100L) |
| Double | IEEE floating point | Approximate; not for currency |
| Decimal | Arbitrary-precision decimal | Money, rates, tax—default for currency fields |
| Boolean | true / false / null | Three-state when uninitialized |
| String | Unicode text | Immutable; use == for value equality |
| Date | Calendar date (no time) | Date.today(), Date.newInstance(y,m,d) |
| Datetime | Date + time + timezone context | Datetime.now(), GMT storage considerations |
| Time | Time of day only | Less common on exam but valid |
| Id | 15- or 18-character Salesforce ID | Case-sensitive 15-char; 18-char is case-insensitive |
| Blob | Binary data | Attachments, callout bodies, Crypto |
String equality: Use == for content comparison in Apex (unlike Java’s reference ==). Use === only when you specifically care about reference identity for objects.
Id type: Prefer Id over String for record identifiers so invalid ID formats fail early and APIs stay clear. 15-character IDs are case-sensitive; 18-character IDs add a checksum suffix and are safer across case-folding systems.
Decimal for Money (Hard Rule)
Currency and precise arithmetic belong on Decimal, not Double:
Decimal unitPrice = 19.99;
Decimal qty = 3;
Decimal total = unitPrice * qty; // 59.97 exactly as Decimal math
// Currency fields on sObjects are Decimal
Opportunity o = new Opportunity(Amount = 1000.50);
Double is fine for scientific or approximate metrics. Exam scenarios that mention invoices, tax, discounts, or Opportunity Amount almost always expect Decimal (or the field type already is Currency → Decimal in Apex).
Scale and rounding matter when you divide:
Decimal share = 100 / 3; // Integer division first if both are Integer!
Decimal shareSafe = 100.0 / 3; // Better: involve Decimal/Double explicitly
Decimal precise = Decimal.valueOf(100).divide(3, 2, RoundingMode.HALF_UP);
Mixing Integer division with money is a classic bug: 100 / 3 is Integer 33, not 33.33. Cast or use Decimal operands before dividing money.
sObject Types
Every standard and custom object is an Apex type. You can:
- Construct with named parameters:
new Contact(LastName = 'Lee', Email = 'lee@example.com') - Read/write fields with dot notation:
c.Email = 'new@example.com' - Hold query results:
Account a = [SELECT Id, Name FROM Account LIMIT 1]; - Use generic
SObjectwhen the type is dynamic
Custom objects and fields use __c suffixes: Invoice__c inv = new Invoice__c(Amount__c = 250);.
Generic sObject access uses get / put:
SObject sob = new Account();
sob.put('Name', 'Generic Name');
String name = (String)sob.get('Name');
Prefer concrete types when you know the object; use SObject for frameworks, dynamic SOQL, and reusable utilities.
Type Conversion and Casting
Apex converts some primitives automatically (widening), but many conversions are explicit:
Integer i = 42;
Long l = i; // widening OK
String s = String.valueOf(i); // Integer → String
Integer parsed = Integer.valueOf('42');
Decimal d = Decimal.valueOf('19.99');
Id accountId = (Id)'001000000000001AAA';
Casting sObjects downcasts must match the runtime type or you get a runtime exception:
SObject sob = [SELECT Id, Name FROM Account LIMIT 1];
Account a = (Account)sob; // OK if sob is Account
valueOf methods throw when the string is not parseable—handle or validate input rather than assuming clean data from users or integrations.
Null Behavior and Safe Navigation
Null is a first-class value. Common patterns:
- Optional fields on queried records may be null
- Maps return null when a key is missing (
map.get(id)) - Method chains like
acct.Owner.Namefail if any segment is null
String ownerName = acct.Owner?.Name; // safe navigation: null if Owner is null
if (acct != null && acct.Name != null) {
// guarded use
}
Safe navigation (?.) short-circuits to null instead of throwing. It is excellent for display and defensive coding. Do not use it to hide logic errors when a value is required for business rules—fail clearly when data must exist.
Expressions and Operators
Apex supports familiar arithmetic (+ - * /), comparison (== != < > <= >=), logical (&& || !), and ternary (condition ? a : b) operators. String concatenation uses +. Assignment operators (+=, -=) work on numbers and strings as expected.
Boolean short-circuiting applies: in a && b(), b() is not called if a is false—useful when b() would null-dereference.
instanceof checks runtime type:
if (sob instanceof Account) {
Account a = (Account)sob;
}
Putting It Together for the Exam
When a question shows a snippet, ask:
- What is each variable’s type, including null?
- Is money on Decimal or accidentally on Double/Integer division?
- Will a cast or
valueOfthrow on bad input? - Does field access assume a non-null relationship?
Mastering these basics prevents missing “easy” items so you can spend time on triggers, bulkification, and governor limits in later chapters.
A developer needs to store an invoice line total that must remain exact for currency calculations. Which Apex type should they use?
What happens when an Apex local Integer variable is declared without an initializer and then used in arithmetic?
Which statement about the final keyword in Apex is correct?