# Enums and Records

Course: [Java](https://www.devtutorials.ai/learn/java) · Section: Object-Oriented Programming
Source: https://www.devtutorials.ai/learn/java/object-oriented-programming/enums

> Model fixed sets of choices with enums, including fields, constructors and methods, and carry immutable data with records.

Think about the sizes printed on a T-shirt label: S, M, L and XL.

A shop never sells size "Medum" or size "7". There is a short, fixed list, and every shirt must use one of those sizes.

Now think about a printed receipt. It holds a small set of values, like an item, a price and a date. Once printed, it does not change. You only read it.

Java has a special type for each idea. An **enum** is a fixed list of named values, like shirt sizes. A **record** is a simple data holder for values that never change, like a receipt.

In simple words: use an enum for a fixed set of choices, and use a record to carry a small group of values that should stay the same.

## What You Will Learn

In this lesson, you will learn:

1. Why enums are safer than plain constants.
2. How to declare an enum and use its constants.
3. The built-in enum methods, such as `values()` and `valueOf()`.
4. How to give enums fields, constructors and methods.
5. Why records exist, and how to declare one.
6. What Java writes for you in a record.
7. When to use a record and when to use a normal class.

## Start With a Working Example

Press **Run**.

```java runnable title="An enum for sizes, a record for an order line"
public class Main {
    public static void main(String[] args) {
        OrderLine line = new OrderLine("T-shirt", Size.L, 499);

        System.out.println(line);
        System.out.println("Size chosen: " + line.size());
        System.out.println("Is it large? " + (line.size() == Size.L));
    }
}

enum Size {
    S, M, L, XL
}

record OrderLine(String item, Size size, int price) {
}
```

Expected output:

```text
OrderLine[item=T-shirt, size=L, price=499]
Size chosen: L
Is it large? true
```

`Size` allows only four values. `OrderLine` holds three values, and Java wrote its constructor, its accessor methods and its readable `toString()` for you.

### Try This

Change `Size.L` to `Size.XXL`. The program no longer compiles, because `XXL` is not in the list. That is exactly the safety an enum gives you.

## Enumerations (enum)

### Why Enums Are Needed

Before enums, programmers used plain constants for fixed choices:

```text
static final int SMALL = 1;
static final int MEDIUM = 2;
static final int LARGE = 3;

int size = 7;   // compiles, but 7 means nothing
```

This has real problems:

- **Any number is accepted.** Nothing stops `size = 7`, or `size = -1`.
- **Values mix up.** A method expecting a size will happily take a day number.
- **Printing shows numbers.** `System.out.println(size)` prints `2`, not `MEDIUM`.

An **enum** (short for **enumeration**) fixes all three. It is a type whose only possible values are the ones you list.

### Enum Declaration

You declare an enum with the `enum` keyword, then list its values, separated by commas.

```text
enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
```

An enum is a special kind of class. You can declare it in its own file, or next to another class.

### Enum Constants

Each value in the list is an **enum constant**. By convention, constants are written in **UPPER_CASE**.

You use a constant with the enum's name and a dot: `Day.MONDAY`.

```java runnable title="Enum constants are the only allowed values"
public class Main {
    public static void main(String[] args) {
        Status orderStatus = Status.SHIPPED;

        System.out.println("Status: " + orderStatus);

        if (orderStatus == Status.SHIPPED) {
            System.out.println("Your order is on the way.");
        }

        switch (orderStatus) {
            case PLACED:
                System.out.println("We have your order.");
                break;
            case SHIPPED:
                System.out.println("Track it with your order ID.");
                break;
            case DELIVERED:
                System.out.println("Enjoy your purchase!");
                break;
        }
    }
}

enum Status {
    PLACED, SHIPPED, DELIVERED
}
```

Expected output:

```text
Status: SHIPPED
Your order is on the way.
Track it with your order ID.
```

Printing an enum shows its name, not a number.

Enum constants can be compared with `==`. There is exactly one `Status.SHIPPED` object in the whole program, so `==` is safe here.

In a `switch`, write the constant without the enum name: `case SHIPPED`, not `case Status.SHIPPED`.

### Enum Methods

Every enum gets some useful methods automatically:

| Method | What it does | Example | Result |
|---|---|---|---|
| `values()` | Returns all constants as an array, in order | `Status.values()` | `[PLACED, SHIPPED, DELIVERED]` |
| `valueOf(text)` | Turns text into the matching constant | `Status.valueOf("PLACED")` | `Status.PLACED` |
| `name()` | Returns the constant's name as text | `Status.SHIPPED.name()` | `"SHIPPED"` |
| `ordinal()` | Returns the constant's position, from 0 | `Status.SHIPPED.ordinal()` | `1` |
| `compareTo(other)` | Compares positions | `PLACED.compareTo(DELIVERED)` | negative |

```java runnable title="Use values(), name(), ordinal() and compareTo()"
public class Main {
    public static void main(String[] args) {

        // values()
        for (Season s : Season.values()) {

            // name() + ordinal()
            System.out.println(
                s.name() + " -> Position: " + s.ordinal()
            );
        }

        // compareTo()
        System.out.println();

        System.out.println(
            "SUMMER vs WINTER = "
            + Season.SUMMER.compareTo(Season.WINTER)
        );

        System.out.println(
            "WINTER vs SUMMER = "
            + Season.WINTER.compareTo(Season.SUMMER)
        );
    }
}

enum Season {
    SUMMER,
    MONSOON,
    AUTUMN,
    WINTER
}
```

Expected output:

```text
SUMMER -> Position: 0
MONSOON -> Position: 1
AUTUMN -> Position: 2
WINTER -> Position: 3

SUMMER vs WINTER = -3
WINTER vs SUMMER = 3
```

`values()` makes it easy to show every choice, for example in a menu.

`valueOf()` must get the exact name, in the same case. `Season.valueOf("monsoon")` stops the program with an `IllegalArgumentException`.

Do not save `ordinal()` values in files or databases. If someone later adds a new constant in the middle of the list, every position after it shifts, and the saved numbers point to the wrong values. Save `name()` instead.

### Enum Constructors

An enum can have **fields**, a **constructor** and **methods**, just like a class. This lets each constant carry its own data.

You put the data in brackets after each constant. The list then ends with a `;`, followed by the fields and the constructor.

```text
enum Coin {
    ONE(1), TWO(2), FIVE(5);

    private final int value;

    Coin(int value) {
        this.value = value;
    }
}
```

Rules for enum constructors:

- They are always `private`. You cannot write `new Coin(10)`. Java creates each constant once, by itself.
- They run once for each constant, when the enum is first used.

```java runnable title="Each constant carries its own price and label"
public class Main {
    public static void main(String[] args) {
        for (Plan p : Plan.values()) {
            System.out.println(p.label() + ": Rs. " + p.monthlyPrice()
                    + " a month, Rs. " + p.yearlyPrice() + " a year");
        }

        Plan chosen = Plan.STANDARD;
        System.out.println("You chose " + chosen.label());
    }
}

enum Plan {
    BASIC("Basic", 199),
    STANDARD("Standard", 499),
    PREMIUM("Premium", 799);

    private final String label;
    private final int monthlyPrice;

    Plan(String label, int monthlyPrice) {
        this.label = label;
        this.monthlyPrice = monthlyPrice;
    }

    String label() {
        return label;
    }

    int monthlyPrice() {
        return monthlyPrice;
    }

    int yearlyPrice() {
        return monthlyPrice * 10;
    }
}
```

Expected output:

```text
Basic: Rs. 199 a month, Rs. 1990 a year
Standard: Rs. 499 a month, Rs. 4990 a year
Premium: Rs. 799 a month, Rs. 7990 a year
You chose Standard
```

`BASIC("Basic", 199)` calls the constructor with those two values. Each constant now knows its own label and price.

`yearlyPrice()` is a normal method. It works the same way on every constant, using that constant's own data.

### Enums vs Constants

| | `static final int` constants | `enum` |
|---|---|---|
| Can hold an invalid value? | Yes, any `int` | No, only the listed constants |
| Can mix up different groups? | Yes, every group is just `int` | No, each enum is its own type |
| Prints as | A number, like `2` | A name, like `MEDIUM` |
| Can list all values? | No | Yes, with `values()` |
| Can carry extra data and methods? | No | Yes |
| Works in `switch` | Yes | Yes |

**Easy Way to Remember:** if you can write the full list of choices on a sticky note, and it rarely changes, use an enum.

## Records

### Why Records Are Needed

Many classes exist only to **carry data**: a point with `x` and `y`, a student with a name and marks, an API response with a status and a message.

Writing such a class by hand takes a lot of code. You need:

- `private final` fields;
- a constructor that sets them;
- an accessor method for each field;
- `equals()` and `hashCode()`, so two objects with the same data count as equal;
- `toString()`, so the object prints its values.

That is often 30 or more lines for something simple. It is easy to get wrong, too, for example by forgetting to update `equals()` when a field is added.

A **record** does all of this in one line. Records were added in Java 16.

### Record Declaration

You declare a record with the `record` keyword, a name, and a list of components in brackets.

```text
record Point(int x, int y) { }
```

That single line gives you a complete, immutable class.

### Record Components

The values inside the brackets are called **record components**. For each component, Java creates:

- a `private final` field with the same name;
- a public **accessor** method with the same name, like `x()`.

Notice that the accessor is `x()`, not `getX()`.

Records are **immutable**. Every field is `final`, and there are no setter methods. To "change" a record, you create a new one.

```java runnable title="A one-line record, used like a full class"
public class Main {
    public static void main(String[] args) {
        Student a = new Student("Asha", 92);
        Student b = new Student("Asha", 92);
        Student c = new Student("Ravi", 78);

        System.out.println(a);
        System.out.println(a.name() + " scored " + a.marks());
        System.out.println("a equals b? " + a.equals(b));
        System.out.println("a equals c? " + a.equals(c));
    }
}

record Student(String name, int marks) {
}
```

Expected output:

```text
Student[name=Asha, marks=92]
Asha scored 92
a equals b? true
a equals c? false
```

`a` and `b` are two separate objects. But they hold the same data, so the record's automatic `equals()` says they are equal.

A normal class would print something like `Student@1b6d3586`, and `equals()` would only be true for the very same object.

Records were added in Java 16. They are a short way to write a final class whose fields are all `private final`. A record can still implement interfaces and have static methods.

### Record Methods

A record can have its own methods, just like a class.

It can also have a **compact constructor**. This is a constructor with no parameter list after the name. It runs before the fields are set, so it is the place to check the values.

```java runnable title="A record with a check and a helper method"
public class Main {
    public static void main(String[] args) {
        Rectangle r = new Rectangle(4, 6);
        System.out.println(r);
        System.out.println("Area: " + r.area());
        System.out.println("Square? " + r.isSquare());

        Rectangle bigger = r.scale(2);
        System.out.println("Scaled: " + bigger);
        System.out.println("Original: " + r);
    }
}

record Rectangle(int width, int height) {

    Rectangle {
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException("Sides must be positive");
        }
    }

    int area() {
        return width * height;
    }

    boolean isSquare() {
        return width == height;
    }

    Rectangle scale(int factor) {
        return new Rectangle(width * factor, height * factor);
    }
}
```

Expected output:

```text
Rectangle[width=4, height=6]
Area: 24
Square? false
Scaled: Rectangle[width=8, height=12]
Original: Rectangle[width=4, height=6]
```

The compact constructor rejects bad sizes. `new Rectangle(0, 5)` would stop the program with an `IllegalArgumentException`.

`scale()` returns a **new** record. The original `r` is unchanged, because records are immutable.

`throw` stops the program with an error. You will learn how to handle errors in a later chapter.

### What Java Writes for You

| For `record Point(int x, int y)` | Written automatically |
|---|---|
| Fields | `private final int x;` and `private final int y;` |
| Constructor | `Point(int x, int y)` |
| Accessors | `x()` and `y()` |
| `equals()` | True when both `x` and `y` match |
| `hashCode()` | Matches `equals()` |
| `toString()` | `Point[x=3, y=4]` |

### Records vs Classes

| | Record | Normal class |
|---|---|---|
| Main job | Carry data | Data plus behaviour |
| Fields | All `private final`, set once | Any kind, can change |
| Setters | None | You can write them |
| `equals()`, `hashCode()`, `toString()` | Written for you | You write them yourself |
| Can extend another class? | No | Yes |
| Can be extended? | No, records are final | Yes, unless `final` |
| Can implement interfaces? | Yes | Yes |
| Length | Often one line | Often 30+ lines for the same data |

Use a record when an object is just a group of values that should not change: coordinates, money amounts, results, settings and messages.

Use a normal class when the object's data changes over time, like a bank account balance, or when you need inheritance.

## Common mistakes

The following mistakes are common when using enums and records. Each row shows what you see, why it happens and how to fix it.

| Symptom | Cause | Fix |
|---|---|---|
| `IllegalArgumentException: No enum constant` | `valueOf()` got text that is not an exact name | Match the case, or use `toUpperCase()` |
| `';' expected` inside an enum | The constant list was not closed before the fields | End the constant list with `;` |
| `enum types may not be instantiated` | `new` was used on an enum | Use an existing constant, like `Plan.BASIC` |
| `cannot find symbol: method getX()` | Bean-style getter used on a record | Use the accessor `x()` |
| `cannot assign a value to final variable` | A record field was changed | Create a new record with the new value |
| Saved data points to the wrong constant | `ordinal()` was stored and constants were reordered | Store `name()` instead |

## What to Remember

- An enum is a type with a fixed list of named constants. Only those values are allowed.
- Use constants with the enum name, like `Status.SHIPPED`, and compare them with `==`.
- `values()`, `valueOf()`, `name()` and `ordinal()` come with every enum.
- Enums can have fields, private constructors and methods, so each constant can carry its own data.
- A record is a one-line, immutable data class. Java writes its constructor, accessors, `equals`, `hashCode` and `toString`.
- Record accessors are named `x()`, not `getX()`. Use a compact constructor to check values.
- Use a record for plain data that never changes. Use a class when data changes or you need inheritance.

Next: Interfaces
