LearnFrenzy
Sign in Take a free test

Data Types, Strings, Dates and Operators · Lesson 4 of 41

Apex Variables and Data Types

6 min read Updated Nov 8, 2026 Saurabh Samir

Apex is strongly typed: every variable is declared with a type, and the compiler checks every assignment. That catches whole classes of bugs before your code ever runs – but you need to know what each type can hold, how it behaves at the edges, and how to convert between types. This lesson covers all of it in one place.

In this lesson

  • Declaring variables and constants
  • The primitive types and their ranges
  • null, the default for everything
  • Decimal vs Double, Integer division and overflow
  • The Id type and type conversion

Declaring variables

Integer quantity = 3;                     // type name value;
String productName = 'Laptop Stand';
Boolean inStock = true;
Decimal unitPrice = 1499.99;
final Decimal GST_RATE = 0.18;            // final: can be assigned only once

Integer reorderLevel;                     // declared, not assigned
System.debug('Reorder level: ' + reorderLevel);

Decimal total = (quantity * unitPrice * (1 + GST_RATE)).setScale(2);
System.debug(productName + ' x' + quantity + ' = ' + total);

Debug log

Reorder level: null
Laptop Stand x3 = 5309.96

Every variable that isn't assigned is null – including Integers and Booleans. Unlike Java, Apex has no "0 by default" for numbers. Using a null number in arithmetic throws a NullPointerException, so initialise counters explicitly: Integer count = 0;.

The primitive types

TypeHoldsExample
Integer32-bit whole number, −2,147,483,648 to 2,147,483,647Integer seats = 25;
Long64-bit whole numberLong views = 9000000000L;
Decimalexact decimal number – the type of Currency and Number fieldsDecimal price = 19.99;
Double64-bit floating point – approximateDouble ratio = 0.333;
Booleantrue, false – or nullBoolean isActive = true;
Stringtext of any length (within heap limits)String city = 'Pune';
Ida valid 15- or 18-character Salesforce record IdId accId = acc.Id;
Date, Datetime, Timecalendar dates, moments in time, times of dayDate d = Date.newInstance(2026, 11, 8);
Blobbinary data – files, encrypted valuesBlob b = Blob.valueOf('text');
Objectany value; cast it back to use itObject o = 42;

Numbers: the traps

// 1. Integer division drops the remainder
Integer a = 7, b = 2;
System.debug('7 / 2 as Integer: ' + (a / b));
System.debug('7 / 2 as Decimal: ' + (Decimal.valueOf(a) / b));

// 2. Integer overflow wraps around silently
Integer big = 2147483647;
big = big + 1;
System.debug('Integer max + 1: ' + big);
Long safe = 2147483647L + 1;
System.debug('As a Long: ' + safe);

// 3. Decimal is exact, Double is not
Decimal d1 = 0.1, d2 = 0.2;
Double f1 = 0.1, f2 = 0.2;
System.debug('Decimal 0.1 + 0.2 = ' + (d1 + d2));
System.debug('Double  0.1 + 0.2 = ' + (f1 + f2));

// 4. rounding money
Decimal amount = 1234.5678;
System.debug(amount.setScale(2) + ' | ' + amount.setScale(0, RoundingMode.DOWN) + ' | ' + amount.round(System.RoundingMode.HALF_UP));

Debug log

7 / 2 as Integer: 3
7 / 2 as Decimal: 3.5
Integer max + 1: -2147483648
As a Long: 2147483648
Decimal 0.1 + 0.2 = 0.3
Double  0.1 + 0.2 = 0.30000000000000004
1234.57 | 1234 | 1235
  • Use Decimal for money and anything stored in Currency, Number or Percent fields. Use Double only for scientific values where tiny errors don't matter.
  • When one side of a division is Decimal, the result is Decimal. Convert before dividing, not after.
  • Overflow doesn't throw – it wraps to a negative number. Use Long for counters that could exceed two billion (byte sizes, timestamps in milliseconds).

Booleans can be null

Boolean optedIn;                           // null – neither true nor false
System.debug('optedIn == true? ' + (optedIn == true));
System.debug('optedIn == false? ' + (optedIn == false));

try {
    if (optedIn) {                         // a null Boolean in a condition throws
        System.debug('never printed');
    }
} catch (NullPointerException e) {
    System.debug('Error: ' + e.getMessage());
}

Boolean safe = optedIn == true;           // treats null as false
System.debug('safe: ' + safe);

Debug log

optedIn == true? false
optedIn == false? false
Error: Attempt to de-reference a null object
safe: false

Checkbox fields on records are never null (they default to false), but Boolean variables, wrapper properties and JSON values can be. Comparing with == true is the simple defence.

The Id type

An Id is a String that Salesforce has validated as a record Id. Its first three characters identify the object (001 = Account, 003 = Contact, 006 = Opportunity):

Account acc = new Account(Name = 'Id Demo');
insert acc;

Id accountId = acc.Id;
System.debug('18-char Id: ' + accountId);
System.debug('15-char Id: ' + String.valueOf(accountId).left(15));
System.debug('Object type: ' + accountId.getSObjectType());

Id fromShort = String.valueOf(accountId).left(15);   // 15 → 18 conversion is automatic
System.debug('Same record? ' + (fromShort == accountId));

try {
    Id broken = 'not-an-id';
} catch (StringException e) {
    System.debug('Invalid: ' + e.getMessage());
}

Debug log

18-char Id: 0015g0000000001AAA
15-char Id: 0015g0000000001
Object type: Account
Same record? true
Invalid: Invalid id: not-an-id

(Record Ids in this course's output are replaced with stable sample values, since every run creates new records.) Use the Id type for record Ids rather than String: invalid values fail immediately, 15- and 18-character versions compare as equal, and getSObjectType() tells you which object an Id belongs to – handy for polymorphic code.

Type conversion

// String → number
Integer qty = Integer.valueOf('42');
Decimal price = Decimal.valueOf('199.50');
System.debug(qty + 1);
System.debug(price * 2);

// number → String
String label = String.valueOf(qty) + ' units';
System.debug(label);

// String → Boolean / Date
System.debug(Boolean.valueOf('TRUE'));
System.debug(Date.valueOf('2026-11-08'));

// widening is automatic, narrowing needs a method
Long l = qty;                                // Integer → Long: automatic
Integer back = l.intValue();                  // Long → Integer: explicit
Integer rounded = price.intValue();           // Decimal → Integer: drops decimals
System.debug(back + ' ' + rounded);

// Object needs a cast
Object anything = 'hello';
if (anything instanceof String) {
    String s = (String) anything;
    System.debug(s.toUpperCase());
}

try {
    Integer bad = Integer.valueOf('12abc');
} catch (TypeException e) {
    System.debug('TypeException: ' + e.getMessage());
}

Debug log

43
399.00
42 units
true
2026-11-08 00:00:00
42 199
HELLO
TypeException: Invalid integer: 12abc

Practice

Try it yourself

  1. A discount is calculated as Integer percent = discountAmount / listPrice * 100; with Integer values 150 and 1000, and comes out as 0. Fix it.

    Show answer

    Integer division gives 150 / 1000 = 0 before multiplying. Use Decimal: Decimal percent = (Decimal.valueOf(discountAmount) / listPrice * 100).setScale(2); – or multiply first: discountAmount * 100 / listPrice (still Integer, 15).

  2. Why should a method parameter for a record Id be typed Id rather than String?

    Show answer

    Invalid values are rejected at the boundary with a clear error, 15/18-character forms compare equal, and you can call getSObjectType() on it.

Quick check

What is the default value of an unassigned Integer?

null.

Which type should hold currency amounts?

Decimal.

What is 7 / 2 when both are Integers?

3 – the remainder is dropped.

Summary

  • Every variable is typed; unassigned variables are null, even numbers and Booleans.
  • Use Decimal for money, Long for very large counts; watch Integer division and overflow.
  • Use the Id type for record Ids.
  • Convert with valueOf methods, and cast Object values after checking instanceof.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Popular