# NOTES — six files, and the two shapes an enum can take

## Run it first

```
.toolchain\jdk-21\bin\javac.exe -d out *.java
.toolchain\jdk-21\bin\java.exe -cp out Main
```

Real output, verbatim:

```
--- 1. a constant carries its own value
  NICKEL -> 5
  DIME -> 10
  QUARTER -> 25
  DOLLAR -> 100
  largest first  : [DOLLAR, QUARTER, DIME, NICKEL]
--- 2. one constant, one instance, forever
  valueOf("QUARTER") == QUARTER : true
  QUARTER.equals(QUARTER)       : true
  distinct instances            : 4
--- 3. the side table, one denomination out of date
  three known coins total : 35
  java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null
--- 4. the field cannot be out of date
  four denominations, one line : 135
  count(NICKEL) with none held : 0
  has an entry per denomination: false
--- 5. iteration order
  inserted        : [QUARTER, NICKEL, DIME]
  EnumMap  order  : [NICKEL, DIME, QUARTER]
  HashMap  order  : [QUARTER, NICKEL, DIME]   (unspecified)
--- 6. values() is a fresh array every call
  values() == values() : false
  after overwriting it : [NICKEL, DIME, QUARTER, DOLLAR]
--- 7. ordinal() versus name()
  NICKEL ordinal=0
  DIME ordinal=1
  QUARTER ordinal=2
  DOLLAR ordinal=3
  DOLLAR.ordinal() persisted today : 3
  read back as                     : DOLLAR
--- 8. valueOf on a name that is not there
  java.lang.IllegalArgumentException: No enum constant Coin.HALF_DOLLAR
  java.lang.IllegalArgumentException: No enum constant Coin.quarter
--- 9. each state answers for itself
  state            INSERT_COIN  SELECT   COLLECT   REFUND   busy
  IDLE             true         true     false     true     false
  ACCEPTING_MONEY  true         true     false     true     false
  DISPENSING       false        false    true      false    true
--- 10. what a constant actually is
  Coin extends              : java.lang.Enum
  QUARTER.getClass()        : Coin
  Coin is final             : true
  MachineState is abstract  : true
  IDLE.getClass()           : MachineState$1
  IDLE.getDeclaringClass()  : MachineState
```

Everything above reproduces except one line. **The `HashMap order` line in block 5 is not a
guarantee.** It came out in insertion order on this run, which proves nothing: `HashMap` places enum
keys by their identity hash, and no order is documented. An order you cannot rely on is worse than a
wrong one, because your test passes.

Note `out/` afterwards:

```
Coin.class            MachineEvent.class      MachineState$2.class   Main.class
CoinPurse.class       MachineState.class      MachineState$3.class
LooseCoinPurse.class  MachineState$1.class
```

**Nine class files from six source files.** `MachineState$1`, `$2` and `$3` are the three constants
with bodies, compiled as three anonymous subclasses. That is the strongest single answer to "an enum
is a named integer": three of them are on your disk with their own bytecode.

---

## `Coin.java` — the shape to memorise

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

**Four constructor calls, run once, at class initialisation.** Each one produces an object, and that
object is what `Coin.NICKEL` refers to for the life of the JVM. The invariant the JVM holds for you:
class initialisation happens once, under a lock, before any thread can observe a constant. So an
enum constant is a lazily initialised singleton with no double-checked locking and no volatile field
anywhere. That is why `enum` is the recommended way to write a singleton in Java.

**The semicolon after the last constant is what lets members follow.** Without a member section the
comma-separated list can end bare, and that is why `MachineEvent.java` is one line.

```java
    private final int minorValue;
```

**`final`, so the value is fixed before anything can read it, and `private`, so nothing can reach
around the accessor.** Drop `final`, add a setter, and you have built a mutable singleton. One object
shared by the whole process, and any caller can change what a quarter is worth for everyone. That
failure has no local reproduction, because the corruption happens in whichever code touched it first.

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

**No access modifier, and adding `public` is a compile error:**

```
PublicCtor.java:4: error: modifier public not allowed here
    public PublicCtor(int minorValue) { this.minorValue = minorValue; }
           ^
1 error
```

An enum constructor is private by construction. That is not a style rule the compiler is enforcing;
it is the mechanism that makes the four constants the complete set. `new Coin(7)` is refused for the
same reason:

```
Bad.java:13: error: enum classes may not be instantiated
        return new Coin(7);
               ^
```

**What this constructor guarantees that a lookup table cannot.** A fifth constant cannot be declared
without an argument, because there is no no-argument constructor to bind to. So "a coin exists whose
value nobody wrote down" is not a state the program can reach. Compare with `MINOR_VALUE` in
`LooseCoinPurse.java`, where exactly that state is one edit away and compiles silently.

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

**A method, not a public field, so the coin can start computing the answer later without any caller
changing.** Java has no properties and no way to make a field into a method compatibly, so the
accessor is the cheap decision that keeps that door open. Naming it `minorValue()` rather than
`getMinorValue()` is the corpus convention and matches what `record` generates.

---

## `LooseCoinPurse.java` — the version that treats a constant as a name

Not a straw man. The table is typed, immutable, and it was correct on the day it was written:

```java
    private static final Map<Coin, Integer> MINOR_VALUE = Map.of(
            Coin.NICKEL, 5,
            Coin.DIME, 10,
            Coin.QUARTER, 25);
```

**Nothing here failed to compile when `DOLLAR` was declared**, because a map has no opinion about
which keys it ought to contain. The failure surfaces at the till, in `totalMinor`:

```
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null
```

Read the message: it names the unboxing, the call that returned null and nothing about coins. The
line that produced it is `(long) MINOR_VALUE.get(held.getKey()) * held.getValue()`, and the cast to
`long` is the unboxing site.

**Switching this to `EnumMap` does not fix it, and believing it does is the trap under the trap.**
An `EnumMap` missing an entry returns `null` too — block 11 of the probe run says so:

```
  EnumMap get(DOLLAR): null
```

What fixes it is moving the number onto the constant. `EnumMap` is the right container for *counts*,
which legitimately vary per coin and legitimately start absent. It is the wrong container for facts
that must exist for every constant.

```java
    private final Map<Coin, Integer> counts = new HashMap<>();
```

**A `HashMap` keyed on an enum works and costs more than it needs to.** Every `get` and `put` hashes
the constant's identity, and every count is boxed twice, once as the key's hash input and once as the
`Integer` value. It also gives up iteration order, which is block 5.

---

## `CoinPurse.java` — the reference's container

```java
    private final Map<Coin, Integer> counts = new EnumMap<>(Coin.class);
```

**The `Coin.class` argument is what makes this an array rather than a hash table.** `EnumMap` holds a
`values()` snapshot and one `Object[]` indexed by `ordinal()`, so a lookup is a bounds check and an
array read. No hashing, no key boxing, no collision handling, no resize.

Three guarantees that come with it, in the order they matter:

1. **Iteration is in declaration order**, documented, not incidental. Block 5 inserts
   `[QUARTER, NICKEL, DIME]` and iterates `[NICKEL, DIME, QUARTER]`. Every log line, every
   `toString`, every test assertion over the key set is now reproducible.
2. **A null key throws immediately**, with `Cannot invoke "Object.getClass()" because "key" is null`.
   A `HashMap` accepts null as a key and hides the mistake until something reads it back.
3. **The key space is known**, so `counts.size() == Coin.values().length` is a real completeness
   check. `mentionsEveryCoin()` is that line, and it returns `false` in block 4 because three of four
   denominations are held.

The speed is genuinely better and it is the least important reason. The reason to reach for `EnumMap`
is that the ordering and the null behaviour are decided rather than left open.

```java
    public int count(Coin coin) {
        return counts.getOrDefault(coin, 0);
    }
```

**`getOrDefault` is what makes a partial map safe to expose.** Absence means zero, which is true of a
coin the purse has never seen, so no caller has to handle null and no caller can NPE. That single
decision is why `MachineFactory.standardFloat()` in the corpus can be
`Map.of(NICKEL, 1, DIME, 2, QUARTER, 1)` and survive a new denomination with no edit. The float is a
set of exceptions to zero, not a list to maintain.

```java
        total += (long) held.getKey().minorValue() * held.getValue();
```

**`getKey().minorValue()` is the whole lesson in one expression.** The purse asks the coin. It does
not know a nickel from a dollar and cannot be made stale by a new denomination.

**The cast is on the left operand, before the multiply.** `(long)(a * b)` would multiply two ints,
overflow, and then widen the wrong answer. Not reachable with a realistic coin count, and the habit
costs nothing.

```java
    public List<Coin> denominationsHeld() {
        return List.copyOf(counts.keySet());
    }
```

**`List.copyOf` and not the key set itself.** A `keySet()` is a live view: hand it out and a caller
can call `remove` on it and silently empty the purse. The copy also freezes the order, which is what
makes this method testable.

---

## `MachineState.java` — abstract method per constant

This is the feature with no C++ analogue, and the one that gets over-applied. The syntax first:

```java
    IDLE {
        @Override
        public boolean allows(MachineEvent event) {
            return event != MachineEvent.COLLECT;
        }
    },
```

**The braces after the constant name are a class body.** `IDLE` is an instance of an anonymous
subclass of `MachineState`, which is why block 10 prints `MachineState$1` for `IDLE.getClass()` and
`MachineState` for `IDLE.getDeclaringClass()`. Reach for `getClass()` on an enum with bodies and you
will get the subclass, which breaks any comparison written against the enum class itself.

```java
    public abstract boolean allows(MachineEvent event);
```

**`abstract` is the entire safety mechanism.** It moves "did you decide what this state does?" from a
review comment to a compile error. Add the maintenance state from
`corpus/vending-machine/curveballs/03-maintenance-mode/` and leave it bare:

```
MachineState.java:24: error: MachineState is abstract; cannot be instantiated
    MAINTENANCE;
    ^
1 error
```

Read past the wording. The message sounds like it is about `new`, and it is: a constant declaration
*is* an instantiation, and the class is abstract until every constant supplies the missing body. The
caret is on the constant that forgot. Give the method a default implementation in the enum body
instead of making it abstract, and this error disappears along with the guarantee.

```java
    public final boolean isBusy() {
        return this == DISPENSING;
    }
```

**`final`, because the answer does not vary, and a constant that overrode it would be lying.** Also
note `this == DISPENSING` rather than `equals`: `==` is correct on enums, and it is the convention.

### The threshold, because constant bodies are over-applied

Put behaviour in a constant body when all three hold:

1. **Intrinsic.** The behaviour is a property of the constant, not a decision about it. What a coin is
   worth is intrinsic; what you charge for a chocolate bar is a decision.
2. **Closed.** There is exactly one right answer per constant, and no caller will ever want a
   different one. If a second answer is imaginable, that second answer is a second implementation of
   something, and constant bodies give you nowhere to put it.
3. **Small.** A few lines, no collaborators, no state beyond the constant's own fields. A constant
   body cannot be injected, stubbed, or constructed with a test double, so anything you would want to
   test in isolation does not belong there.

Reach for a policy interface when any one of those fails. The tell is that the behaviour is a
decision that might be swapped, or that you want to test it without the enum.

**What the corpus reference actually chose, in both places.**

`Coin.minorValue` is a field with an accessor, not a constant body. A value is data; a body would be
a method returning a literal, which is the same fact with more syntax.

The transition rules are **not** constant bodies. `corpus/vending-machine/reference/src/` puts them
in `Transitions.standard()` as a `List<Transition>` of `(from, on, to)` records, indexed by
`TransitionTable` into `EnumMap<MachineState, EnumMap<MachineEvent, MachineState>>`. The version in
this lesson is the alternative, and the reference's reasoning is in `TransitionTable`'s own comment.
Read it against the three tests above:

| Test | The `allows` body version | The reference's table |
|---|---|---|
| Intrinsic | Fails. A machine's legal moves are a rule someone wrote, and rules change per deployment |
| Closed | Fails. `MaintenanceTransitions.all()` is a second rule set for the same states |
| Small | Passes, barely. Two of the three bodies are byte-identical, which is the tell |

That duplication is the signal worth learning to see. **When two constant bodies are the same, the
thing varying is not the constant.** IDLE and ACCEPTING_MONEY answer identically because the rule is
about the pair `(state, event)`, and a per-constant method can only be about one of them.

The measured cost, from `curveballs/03-maintenance-mode/reference-patch/PATCH.md`: the reference
absorbs the maintenance mode in **8 changed lines**. Six are declarations every design needs, being
two enum words and two interface methods. The other two are the rule itself, in a new file that is
free under the diff budget.

The constant-body version needs a body for `MAINTENANCE`, plus an edit to `IDLE`'s body to let the
machine leave service. And it cannot express a second rule set at all without a flag inside every
body.

One honest point the other way: the constant-body version is refused at compile time if you forget
the new state's rules, and the table is not. The reference accepts that, because a state no
transition mentions can never be entered, so an incomplete table is unreachable rather than wrong.

---

# Four experiments, run for real

## 1 · The exhaustive switch, and the half of it that is silent

`corpus/parking-lot/reference/src/Footprint.java` maps a vehicle type to how many spots it takes,
using a switch expression with no `default`. Its comment claims a new `VehicleType` stops the file
compiling. Adding `VAN` to `VehicleType`:

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

Claim verified. Two things the message does not do: it does not name `VAN`, and it does not point at
the case labels. It gives you the file, the line and the selector expression.

Then the same three cases as a switch *statement* with a mutable local, compiled with `-Xlint:all`:

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

**Zero errors, zero warnings, and a van that fits in a car space.** Exhaustiveness is required of
switch *expressions* only. A `default` branch buys the same silence back, because every future
constant is already covered by it. That is the argument for leaving `default` off when the constant
set is closed and you want the compiler to tell you.

## 2 · `values()` hands out a copy, every call

```
  values() == values() : false
  after overwriting it : [NICKEL, DIME, QUARTER, DOLLAR]
```

The array is cloned on each call, so a caller who mutates it corrupts their own copy and nothing
else. The invariant is worth the cost: there is no way to damage the constant set from outside.

The cost is an allocation per call, which matters in one place. `for (Coin c : Coin.values())` inside
a hot loop allocates an array per iteration of the outer loop. When that shows up in a profile, hold
one `private static final Coin[] VALUES = values();` and iterate that. `EnumSet.allOf` and `EnumMap`
already do this internally, which is one more reason to reach for them.

## 3 · A persisted `ordinal()` after the coin curveball

`curveballs/01-a-new-coin/contract-delta/Coin.java` inserts `HALF_DOLLAR(50)` between the quarter and
the dollar. Compiling and running the same loop against it:

```
  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 stored yesterday holding `3` for a dollar reads back as a half-dollar today. No exception, no
log line, and every reconciliation from before the deploy is now wrong by fifty cents.

`name()` survived the same edit because it is the identifier you typed. So: `ordinal()` is for
`EnumMap` and `EnumSet` inside one process, `name()` is for anything that outlives it.

## 4 · The sentinel constant, imported from C++

`COIN_COUNT` as a final enumerator is ordinary C++ and a bug here:

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

`GreedyChangeMaker` would try to pay change with it, `EnumSet.allOf` would include it, and every
exhaustive switch would demand a case for it. The number you wanted is `Coin.values().length`.

---

## What an interviewer is measuring

The reason this item is in the bridge track rather than the syllabus is that it is Java mechanics.
The reason it has scoring consequences is the curveball. A new coin denomination costs the reference
**zero lines**, measured in `curveballs/01-a-new-coin/reference-patch/PATCH.md`. It costs nothing
because the value lives on the constant, the change-maker derives its denominations from `values()`,
and the purse is an `EnumMap`. Every plausible alternative is an edit per site: a `switch (coin)` for
the value, an `int[]` of denominations, a float declared with an entry per coin.

Say the reason out loud when you write it. "The value lives on the constant so a new denomination
cannot be declared without one" is a design sentence. "I used an enum" is not.
