# From C++ — the four operators that became four methods

In C++ you overload operators and value comparison works everywhere. Java has no operator
overloading, and the replacements are ordinary methods that the standard library calls by name.
Fail to write them and the library calls `Object`'s versions instead, which answer a different
question.

## `operator==` becomes `equals(Object)`

**C++**

```cpp
class Registration {
    std::string code_;
public:
    explicit Registration(std::string code) : code_(std::move(code)) {}

    bool operator==(const Registration& other) const { return code_ == other.code_; }
};

Registration a{"KA01AB1234"};
Registration b{"KA01AB1234"};
a == b;                                    // true
std::vector<Registration> v{a};
std::find(v.begin(), v.end(), b) != v.end();   // true
```

**Java**

```java
public final class Registration {

    private final String code;

    public Registration(String code) { this.code = code; }

    @Override
    public boolean equals(Object other) {
        if (this == other) return true;
        if (!(other instanceof Registration that)) return false;
        return code.equals(that.code);
    }
}
```

```java
Registration a = new Registration("KA01AB1234");
Registration b = new Registration("KA01AB1234");
a == b;              // false, always, and no override can change it
a.equals(b);         // true, because of the method above
```

| C++ | Java |
|---|---|
| `a == b` calls your `operator==` | `a == b` compares references. Not overridable |
| Omit `operator==` and `a == b` is a compile error | Omit `equals` and `a.equals(b)` compiles, returning `a == b` |
| `const Registration&` parameter | `Object` parameter. Narrowing it makes an overload, not an override |
| `std::find` uses `operator==` | `List.contains` uses `equals` |

**The two rows that cost hours.** In C++ the compiler stops you at the comparison site when
`operator==` is missing. In Java there is nothing to stop, because `Object` already declares
`equals`. So the missing method is not a compile error — it is a lookup that returns the wrong
answer.

And the parameter type is `Object`, not `Registration`. The natural C++ translation is
`public boolean equals(Registration other)`, which compiles and is a **different method**:

```java
Plate p = new Plate("KA01AB1234");
Plate q = new Plate("KA01AB1234");

p.equals(q);              // true   — the compiler picked your overload
list.contains(q);         // false  — List called Object.equals
p.equals((Object) q);     // false  — same
```

Verified output from that exact code, compiled with `-Xlint:all`, zero warnings. `@Override`
converts it into a compile error:

```
Registration.java:28: error: method does not override or implement a method from a supertype
    @Override
    ^
```

That is the reason to write `@Override` on every `equals` you ever type.

## `std::hash` specialisation becomes `hashCode()`

**C++ — a separate opt-in, and the compiler makes you do it**

```cpp
namespace std {
    template <> struct hash<Registration> {
        size_t operator()(const Registration& r) const { return hash<string>{}(r.code()); }
    };
}
std::unordered_map<Registration, std::string> tickets;   // fails to compile without the above
```

**Java — already inherited, already wrong**

```java
    @Override
    public int hashCode() {
        return code.hashCode();
    }
```

```java
Map<Registration, String> tickets = new HashMap<>();     // compiles either way
```

| C++ | Java |
|---|---|
| No `std::hash` specialisation, no `unordered_map` — compile error | `HashMap` accepts any key. `Object.hashCode` is inherited |
| `operator==` and `hash` are separately declared, both required | `equals` and `hashCode` are separately *overridable*, and only convention links them |

`Object.hashCode` returns a per-instance identity value. So an `equals` without a matching
`hashCode` gives you two equal objects with different hashes, and `HashMap.getNode` compares
hashes before it calls `equals`. The entry is never considered. Real output, one entry in the map:

```
--- 3. equals without hashCode: the list works, the map does not
contains(equal plate)     : true
map.get(equal plate)      : null
set size after 2 equal adds: 2
```

The list is right and the map is wrong, from the same object. This is worse than forgetting
`equals` entirely, because a review and a list-based test both pass.

Plain `javac` says nothing. `javac -Xlint:overrides` does:

```
warning: [overrides] Class HalfEqualRegistration overrides equals, but neither it nor any
superclass overrides hashCode method
```

## `operator<<` becomes `toString()`

**C++**

```cpp
std::ostream& operator<<(std::ostream& os, const Registration& r) {
    return os << "Registration[" << r.code() << "]";
}
```

**Java**

```java
    @Override
    public String toString() {
        return "Registration[" + code + "]";
    }
```

Without it, `Object.toString` returns the class name plus `@` plus the hex identity hash:

```
no override : LooseRegistration@1c20c684
override    : Registration[KA01AB1234]
```

The hex changes between runs, so it is worse than useless in a log or an assertion failure.
Unlike `operator<<`, `toString` is called implicitly by string concatenation, `println`, and every
JUnit failure message — so overriding it changes the output of code you did not write.

## `operator<` becomes `compareTo`, or a `Comparator`

**C++ — one operator serves sorting and ordered containers**

```cpp
bool operator<(const Registration& a, const Registration& b) { return a.code() < b.code(); }

std::sort(v.begin(), v.end());                      // uses operator<
std::map<Registration, std::string> byPlate;        // uses operator<
std::sort(v.begin(), v.end(), byLengthThenCode);    // or an explicit comparator
```

**Java — two mechanisms, and they mean different things**

```java
public final class Registration implements Comparable<Registration> {

    @Override
    public int compareTo(Registration other) {
        return code.compareTo(other.code);
    }
}
```

```java
plates.sort(null);                                              // natural order, via compareTo
plates.sort(Comparator.comparing(Registration::code).reversed()); // one alternative order
Map<Registration, String> byPlate = new TreeMap<>();            // needs Comparable, or a Comparator
```

| C++ | Java |
|---|---|
| `operator<`, returning `bool` | `compareTo`, returning negative/zero/positive `int` |
| Free function or member, your choice | Implementing `Comparable<T>` on the class |
| A comparator functor passed to `std::sort` | A `Comparator<T>` passed to `sort` or to `TreeMap` |
| `std::map` needs a strict weak ordering | `TreeMap` needs `Comparable`, or throws at run time |

`Comparable` is the type's **one** natural order. `Comparator` is every other order, and it lives
outside the type — which is how you sort by three different keys without touching the class.

Omit `Comparable` and a `TreeMap` fails at run time, not compile time:

```
ClassCastException: class LooseRegistration cannot be cast to class java.lang.Comparable
(LooseRegistration is in unnamed module of loader 'app'; java.lang.Comparable is in module
java.base of loader 'bootstrap')
```

Note where the failure lands: on the first `put`, not at the `new TreeMap<>()`, and not at compile
time. `TreeMap<K, V>` places no `extends Comparable` bound on `K`, because a `Comparator` is the
other legal way to supply the order.

**`compareTo` must agree with `equals`.** `TreeMap` uses `compareTo` alone and never calls `equals`.
So a `compareTo` returning 0 for two objects that are not `equals` gives you a `TreeMap` and a
`HashMap` that disagree about whether you have one key or two.

## The shortcut: `record`

The corpus contract uses one:

```java
public record Vehicle(String registration, VehicleType type) {}
```

A `record` generates `equals`, `hashCode` and `toString` from its components, correctly and
consistently. It generates no `compareTo`. So the four-method drill above applies whenever you
write a `class` rather than a `record`. That means whenever the type needs mutable state, extra
invariants, or identity semantics. `record` is J11; the reason to know it now is that it removes
three of the four traps on this page.
