# From C++ — the same four coins, and why one of them can answer questions

`enum class` was the biggest thing C++11 fixed about enums, so it is reasonable to arrive thinking
Java's version is the same idea with a different keyword. It is not the same kind of construct at
all, and this page is the delta.

## The declaration

**C++ — the constant is the number**

```cpp
enum class Coin : int {
    NICKEL  = 5,
    DIME    = 10,
    QUARTER = 25,
    DOLLAR  = 100,
};

int minorValue(Coin c) { return static_cast<int>(c); }
```

The value *is* the enumerator. One number per constant, and no room for a second. The moment a coin
needs two facts about itself you reach for a `switch` in a free function, or a `constexpr` lookup
table indexed by the cast.

**Java — the constant is an object that was handed a number**

```java
public enum Coin {

    NICKEL(5),
    DIME(10),
    QUARTER(25),
    DOLLAR(100);

    private final int minorValue;

    Coin(int minorValue) {
        this.minorValue = minorValue;
    }

    public int minorValue() {
        return minorValue;
    }
}
```

Read the four lines at the top as four constructor calls, because that is what they are.
`Coin.QUARTER` is a reference to an object built by `new Coin(25)`, running at class initialisation,
before any of your code touches it. The reflection in `worked/Main.java` block 10 says so out loud:

```
  Coin extends              : java.lang.Enum
  QUARTER.getClass()        : Coin
```

There is no cast from `Coin` to `int` and no underlying integral type to declare. Try it:

```
Widen.java:2: error: incompatible types: Coin cannot be converted to int
    static int asInt(Coin coin) { return coin; }
                                         ^
```

Room for a second fact costs nothing now. `Slot` in the same corpus problem carries four; a coin
that also needed a display glyph and a weight in grams would take two more constructor parameters
and no new file.

## The delta table

| C++ `enum class` | Java `enum` |
|---|---|
| A distinct integral type with named values | A final class extending `java.lang.Enum`, with a fixed instance set |
| `static_cast<int>(Coin::QUARTER)` is 25 | `Coin.QUARTER.minorValue()` is 25; `ordinal()` is 2 |
| Underlying type declarable: `: int`, `: uint8_t` | No underlying type. A constant is a reference |
| Cannot hold data or methods | Fields, methods, constructors, interfaces |
| No mapping from a name to a value at run time | `name()`, `valueOf(String)`, `values()`, `toString()` |
| `Coin::QUARTER` in a case label | `case QUARTER` bare, or `case Coin.QUARTER` since Java 21 |
| `switch` on an enum warns under `-Wswitch` | Only a switch **expression** errors; a statement is silent |
| Free function + `switch` for per-constant behaviour | A method body on the constant itself |
| `int counts[COIN_COUNT]` with a sentinel constant | `EnumMap<Coin, Integer>`, and **never** a sentinel |
| Can be forward declared with its underlying type | No forward declaration exists, and none is needed |

## Trap 1 · `ordinal()` is not the value, and it moves

The muscle memory to break. In C++ the integer behind the constant is the thing you chose. In Java
it is the position in the declaration list, counting from zero, and you chose nothing.

```
  NICKEL ordinal=0
  DIME ordinal=1
  QUARTER ordinal=2
  DOLLAR ordinal=3
```

The corpus has a curveball that puts the fifty-cent piece back into circulation
(`corpus/vending-machine/curveballs/01-a-new-coin/`). Its revised `Coin` inserts `HALF_DOLLAR(50)`
between the quarter and the dollar. Run the same loop against that file:

```
  NICKEL ordinal=0
  DIME ordinal=1
  QUARTER ordinal=2
  HALF_DOLLAR ordinal=3
  DOLLAR ordinal=4
  ordinal 3 reads back as : HALF_DOLLAR
  name "DOLLAR" reads back as: DOLLAR
  and it is still worth      : 100
```

A row written yesterday holding `3` for a dollar reads back as a half-dollar today. Nothing threw,
nothing logged, and the audit is now wrong by fifty cents per row.

So: **never persist `ordinal()`, and never send it over a wire.** Persist `name()`, which is the
identifier you typed and changes only when you rename the constant. The rule holds for a database
column, a JSON field, a cache key and a file format. `ordinal()` earns its keep inside the process
that produced it, which is exactly what `EnumMap` and `EnumSet` do with it.

## Trap 2 · The sentinel constant that ends every C++ enum

`COIN_COUNT` or `MAX` as a last enumerator is ordinary C++, because you need a size for the array.
In Java it is a coin the machine accepts:

```
  values()      : [NICKEL, DIME, QUARTER, DOLLAR, COUNT]
  largest first : [DOLLAR, QUARTER, DIME, NICKEL, COUNT]
  COUNT is a coin the machine now accepts: 0
```

`values()`, every `for` loop over it, every exhaustive `switch` and every `EnumSet.allOf` now
include it. The size you wanted is `Coin.values().length`, and `new EnumMap<>(Coin.class)` sizes
itself, so the sentinel has no job left to do.

## Trap 3 · `==` is the right comparison here, and only here

J6 spent a page on `==` comparing references and never values. Enums are the exception, and the
reason is mechanical rather than a special case in the language:

```
  valueOf("QUARTER") == QUARTER : true
  distinct instances            : 4
```

The constructor is private, the class cannot be extended, and `valueOf` looks up the existing
instance rather than building one. So two references to the same constant are always the same
object, and `==` is both correct and the convention. `equals` is `final` on `java.lang.Enum` and
delegates to `==` anyway.

Two consequences worth keeping. `==` on enums gives you a compile error on a type mismatch, where
`equals` would silently return `false`. And a `switch` over an enum is legal precisely because the
constant set is closed.

## Trap 4 · `valueOf` throws where you expect a null or a sentinel

C++ has no name-to-constant lookup, so there is no habit to unlearn — there is only a new API to
read correctly.

```
  java.lang.IllegalArgumentException: No enum constant Coin.HALF_DOLLAR
  java.lang.IllegalArgumentException: No enum constant Coin.quarter
```

Both lines are real output from `worked/Main.java`. Note the second: the match is exact and case
sensitive, so a lowercased value from a config file or a JSON body throws. `IllegalArgumentException`
is unchecked, so nothing forces you to handle it and nothing warns you that you have not. Parsing
untrusted text into an enum is the one place to write the try/catch, and the one place a
`values()` scan with your own fallback is worth the lines.

## Trap 5 · The exhaustiveness habit inverts

In C++ you get `-Wswitch` on a plain `switch` statement over an `enum class`, and under `-Werror`
that is your safety net. Java moves the check: it is an error, not a warning, but only for a switch
**expression** — the form that produces a value.

The corpus has the real one. `corpus/parking-lot/reference/src/Footprint.java` line 30:

```java
    public static Footprint of(VehicleType type) {
        return switch (type) {
            case MOTORBIKE -> MOTORBIKE;
            case CAR -> CAR;
            case TRUCK -> TRUCK;
        };
    }
```

Add a fourth vehicle type to `VehicleType` and that file stops compiling:

```
Footprint.java:30: error: the switch expression does not cover all possible input values
        return switch (type) {
               ^
1 error
```

That is the mechanism behind "add a constant, let the compiler find every site", and it is what
makes an interviewer's new vehicle type cheap to absorb. Two details to get right:

**The message does not name the constant you missed.** It names the file, the line and the selector,
and stops there. Read it as "count your cases against `values()`".

**A switch statement doing the same job is silent.** The same three cases, written as a statement
with a mutable local, compiled under `-Xlint:all` with no warning at all and then:

```
  MOTORBIKE occupies 1 spot(s)
  CAR occupies 1 spot(s)
  TRUCK occupies 2 spot(s)
  VAN occupies 1 spot(s)
```

The van silently takes one spot. So prefer the expression form whenever the switch produces a value,
and know that a `default` branch buys the same silence back by making every future constant covered.

## Trap 6 · No inheritance, so a hierarchy has to go elsewhere

```
Extend.java:2: error: cannot inherit from final Coin
Extend.java:2: error: enum classes are not extensible
```

An enum with no constant bodies compiles to a `final` class. Give any constant a body and the class
becomes `abstract` instead, with one anonymous subclass per body:

```
  Coin is final             : true
  MachineState is abstract  : true
  IDLE.getClass()           : MachineState$1
  IDLE.getDeclaringClass()  : MachineState
```

Those subclasses are real files on disk — `MachineState$1.class`, `$2`, `$3`. What you cannot do is
extend an enum yourself, from anywhere. When you want an open set, the tool is an interface with
classes behind it, which is J5 and B1 territory. The enum is for a set that is closed and expected
to stay closed. `Coin` is exactly that, and the corpus file says so: a new denomination arrives as a
new constant here.
