Intermediate 25 min read

Custom Types in Collections: Wrapper Classes

By the end of this lesson, you'll be able to:

  • Build a List, Set, or Map of a custom class rather than a primitive type
  • Explain what a wrapper class is and why it's used with collections
  • Combine several related fields into one object stored inside a collection

Prerequisites: "Choosing List vs Set vs Map"

The limitation of primitive collections

List<String> productNames = new List<String>{'Widget', 'Gadget'};
List<Decimal> productPrices = new List<Decimal>{49.99, 89.99};
  1. List<String> productNames = new List<String>{'Widget', 'Gadget'}; — one list.
  2. List<Decimal> productPrices = new List<Decimal>{49.99, 89.99}; — a second, separate list kept in sync by position only — fragile, since sorting or filtering one list without the other would silently misalign productNames[0] with the wrong price. Nothing enforces that these two lists stay related.

A wrapper class holds related data together

public class ProductSummary {
    public String name;
    public Decimal price;

    public ProductSummary(String name, Decimal price) {
        this.name = name;
        this.price = price;
    }
}

List<ProductSummary> products = new List<ProductSummary>{
    new ProductSummary('Widget', 49.99),
    new ProductSummary('Gadget', 89.99)
};

for (ProductSummary p : products) {
    System.debug(p.name + ': R' + p.price);
}
  1. public class ProductSummary { — this is exactly Module 10's class-and-object pattern, now storing custom objects inside a List.
  2. public String name; — one field.
  3. public Decimal price; — another, permanently paired with the name on the same object.
  4. (blank line)
  5. public ProductSummary(String name, Decimal price) { — the constructor.
  6. this.name = name; — sets the name.
  7. this.price = price; — sets the price.
  8. } — closes the constructor.
  9. } — closes the class.
  10. (blank line)
  11. List<ProductSummary> products = new List<ProductSummary>{ — a list of wrapper objects, not primitives.
  12. new ProductSummary('Widget', 49.99), — one product, name and price together.
  13. new ProductSummary('Gadget', 89.99) — a second.
  14. }; — closes the list literal.
  15. (blank line)
  16. for (ProductSummary p : products) { — loops over the wrapper objects.
  17. System.debug(p.name + ': R' + p.price); — no risk of two separate lists drifting out of sync — each object carries its own data.
  18. } — closes the loop.

A real business example: Support Ticket Dashboard

public class TicketSummary {
    public String subject;
    public String priority;
    public Integer daysOpen;

    public TicketSummary(String subject, String priority, Integer daysOpen) {
        this.subject = subject;
        this.priority = priority;
        this.daysOpen = daysOpen;
    }
}

List<TicketSummary> openTickets = new List<TicketSummary>{
    new TicketSummary('Login failure', 'High', 2),
    new TicketSummary('Slow report load', 'Medium', 5)
};
  1. public class TicketSummary { — a wrapper class with three related fields.
  2. public String subject; — the ticket subject.
  3. public String priority; — its priority.
  4. public Integer daysOpen; — how long it's been open.
  5. (blank line)
  6. public TicketSummary(String subject, String priority, Integer daysOpen) { — the constructor.
  7. this.subject = subject; — sets the subject.
  8. this.priority = priority; — sets the priority.
  9. this.daysOpen = daysOpen; — sets the days open.
  10. } — closes the constructor.
  11. } — closes the class.
  12. (blank line)
  13. List<TicketSummary> openTickets = new List<TicketSummary>{ — a dashboard's worth of tickets.
  14. new TicketSummary('Login failure', 'High', 2), — one ticket, all three fields together.
  15. new TicketSummary('Slow report load', 'Medium', 5) — another.
  16. }; — closes the list literal.

A dashboard displaying open support tickets needs several fields per ticket shown together — exactly the case where a List<sObject> (a real Salesforce record) or a custom wrapper class like TicketSummary (useful when the data doesn't map cleanly to one object) both outperform several parallel primitive lists.

Exercise

Write a wrapper class EmployeeSummary with name and yearsOfService fields, then build a List<EmployeeSummary> with two employees.

Show hint

Give EmployeeSummary a constructor accepting both fields.

Salesforce: ○ Not connected Connect
APEX

Execution Results

Run your code to see the result.

Custom Types in Collections: Wrapper Classes Quiz

1. What problem does a wrapper class solve compared to using several parallel primitive Lists?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

A collection isn't limited to primitives — storing a custom class in a List, Set, or Map (a "wrapper class" pattern) lets each item carry several related pieces of data together.