# From C++ — the same syntax over a completely different mechanism

You know templates, including specialisation and SFINAE. None of that is the risk here. The risk is
that `CodeIndex<Sku>` is spelled the same way in both languages while meaning two unrelated things.
The syntax transfers and the mental model does not.

Names in the compiler output below: `CodeIndex`, `Discounts` and `Erasure` are in `worked/src/`.
`E4` to `E7` are four-line probes that exist to fail, so they are quoted here rather than shipped —
every `.java` file under `lessons/` has to compile.

**A template is a code generator.** `CodeIndex<Sku>` and `CodeIndex<Promotion>` are two classes,
generated at each instantiation, type-checked against the arguments you actually used, and each
holding its own `static` members. Two `.o` sections, deduplicated by the linker if you are lucky.

**A Java generic is one class with the types deleted.** `javac` checks the type arguments, then
throws them away and writes `Object` in the bytecode. One `.class` file, whatever you
parameterise it with. That deletion is called erasure, and every surprise below is a consequence of
it rather than a separate rule to memorise.

Here is the deletion, from `javap -s -p` on the `CodeIndex` in `worked/`:

```
public T require(java.lang.String);
  descriptor: (Ljava/lang/String;)Ljava/lang/Object;

public T[] toArray(java.util.function.IntFunction<T[]>);
  descriptor: (Ljava/util/function/IntFunction;)[Ljava/lang/Object;
```

Two signatures per method. The one `javap` prints first comes from a `Signature` attribute that
`javac` writes for the benefit of other compilers; the `descriptor` is what the JVM dispatches on,
and it says `Object`. Now the caller, `javap -c`:

```
static Sku firstSku(CodeIndex<Sku>);
    Code:
       0: aload_0
       1: ldc           #7                  // String TEA
       3: invokevirtual #9                  // Method CodeIndex.require:(Ljava/lang/String;)Ljava/lang/Object;
       6: checkcast     #15                 // class Sku
       9: areturn
```

`checkcast Sku` at offset 6. Nobody wrote that cast. Generics in Java are a compile-time check plus
inserted casts, and the run time sees a registry of `Object`.

## The delta table

| C++ | Java | What differs, and where it bites |
|---|---|---|
| `template<class T> class CodeIndex` | `class CodeIndex<T>` | One class, not one per instantiation |
| implicit constraints from the body | `<T extends Promotion>` | With no bound, `T` has only `Object`'s methods. There is no duck typing |
| `template<> class CodeIndex<Sku>` | nothing | No specialisation, full or partial. This is the biggest single loss |
| `std::array<T, N>` | nothing | No non-type parameters. Sizes are constructor arguments, checked at run time |
| `if constexpr`, `constexpr`, tag dispatch | nothing | No metaprogramming. A type parameter cannot compute |
| `T x;` as a member | `T x;` fine | Same, because it is a reference to `Object` underneath |
| `new T()`, `T()` | nothing | `error: unexpected type`. Pass a `Supplier<T>` |
| `new T[n]` | nothing | `error: generic array creation`. Pass an `IntFunction<T[]>` |
| `static T cache;` in a template | nothing | `error: non-static type variable T ...`. One class, so one field for everybody |
| `typeid(T).name()` | nothing | The name is gone at run time. Pass a label, or a `Class<T>` token |
| `std::is_same_v<T, Sku>` | nothing | `instanceof List<Promotion>` will not compile |
| `std::vector<int>` | `List<Integer>` | No primitive type arguments, ever. Boxing, and `null` becomes possible |
| `std::vector<Dog>` is unrelated to `std::vector<Animal>` | same | Generics are invariant, as templates are |
| `Dog*[]` converts to `Animal*[]` | `Dog[]` is an `Animal[]` | Java arrays are covariant. The asymmetry with generics is the trap |
| a throwing template | `class E<T> extends Throwable` | `error: a generic class may not extend java.lang.Throwable` |
| errors at the instantiation | errors at the declaration | An error in a generic method is found even if nobody calls it |
| header-only, source required | a `.class` is enough | Separate compilation. The caller never sees the generic's source |
| `const T&` to avoid a copy | irrelevant | Everything is already a reference. There is nothing to copy |

## The five things you can no longer write

Every message below is the real `javac` output, triggered against `worked/`'s `CodeIndex`.

**`new T()`.** `Catalog` has no reason to build a blank `Sku`, but a generic pool or a generic
default would. There is no constructor to call, because `T` is `Object` by the time anything runs:

```
CodeIndex.java:92: error: unexpected type
        return new T();
                   ^
  required: class
  found:    type parameter T
  where T is a type-variable:
    T extends Object declared in class CodeIndex
```

The fix is to make the caller supply the construction: a `Supplier<T>` parameter, or a `Class<T>`
token and `getDeclaredConstructor().newInstance()`. The first is better in every case that is not a
framework.

**`new T[n]`.** Same reason, blunter message:

```
CodeIndex.java:75: error: generic array creation
        T[] out = new T[byCode.size()];
                  ^
```

`CodeIndex.toArray` in `worked/` takes an `IntFunction<T[]>` so the caller writes `Sku[]::new`. The
version that looks right compiles and is wrong:

```java
    return (T[]) byCode.values().toArray();
```

```
CodeIndex.java:86: warning: [unchecked] unchecked cast
        return (T[]) byCode.values().toArray();
                                            ^
  required: T[]
  found:    Object[]
```

A warning, not an error, and the method returns normally. The failure lands elsewhere:

```
  toArrayTheWayItLooks()                        : java.lang.ClassCastException
    message : class [Ljava.lang.Object; cannot be cast to class [LSku;
    thrown at: Erasure.whereTheCastActuallyIs(Erasure.java:84)
```

`Erasure.java:84` is the caller's assignment, not the cast on line 86. The `checkcast` you saw in
the bytecode above is where the failure surfaces, and no frame in the trace names the class that
lied. Recognise this one: an unchecked-cast warning you dismissed becomes a `ClassCastException` in
somebody else's file.

**`instanceof` on a parameterised type.** Asking what is in a list is asking a question the run time
threw away:

```
E5.java:6: error: Object cannot be safely cast to List<Promotion>
        return handedIn instanceof List<Promotion>;
               ^
```

`handedIn instanceof List<?>` does compile, and a `List<Sku>` answers `true` to it. `worked/` prints
that:

```
  a List<Sku> answers `o instanceof List<?>`    : true
  first element's real class                    : Sku
```

So the elements can be checked one at a time and the list cannot be checked at all. That is the
whole of run-time type inspection for generics.

**Two overloads differing only by type argument.** This is the C++ move, one `total` per coupon
kind, and it is the error you will hit first. The code looks fine:

```java
    public static long total(List<PercentageCoupon> coupons, long owedMinor) { ... }
    public static long total(List<FixedAmountCoupon> coupons, long owedMinor) { ... }
```

```
E4.java:10: error: name clash: total(List<FixedAmountCoupon>,long) and total(List<PercentageCoupon>,long) have the same erasure
    public static long total(List<FixedAmountCoupon> coupons, long owedMinor) {
                       ^
```

**"have the same erasure"** is the phrase to learn. Both methods erase to
`total(List, long)`, and a class file cannot hold two methods with one descriptor. Rename one, or
take `List<? extends Promotion>` once and let the promotion decide, which is what the corpus does
with `DiscountStage`.

**A `static` field mentioning `T`.**

```
CodeIndex.java:27: error: non-static type variable T cannot be referenced from a static context
    private static T fallback;
                   ^
```

One class means one static field. A `static long` counter is legal, and it counts every
parameterisation together. `worked/` block 4, after two lookups on the catalogue and two on the
coupon book:

```
  catalogue.lookups()                           : 2
  coupons.lookups()                             : 2
  CodeIndex.lookupsAcrossAllIndexes()           : 4  <- both, added up
```

In C++ that field is per-instantiation and the two numbers stay apart. This is the one on the list
that produces no compile error and no exception. It shows up as a metric that is silently double,
or a lazily built cache that hands `Sku`-shaped answers to code asking about coupons.

## Invariance, and the asymmetry with arrays

`List<PercentageCoupon>` is not a `List<Promotion>`, for the reason it is not a
`vector<Promotion>`: if it were, you could add a `FixedAmountCoupon` to it. `Totals.price` in the
reference declares `List<Promotion>`, and here is what that refuses:

```
E6.java:8: error: incompatible types: List<PercentageCoupon> cannot be converted to List<Promotion>
        Discounts.totalNarrow(percentages, List.of(), 5899);
                              ^
```

Nothing surprising so far. The surprise is that Java arrays do not follow the same rule:

```java
        Sku[] catalogue = { new Sku("TEA", "Loose leaf tea", 450), ... };
        Object[] anything = catalogue;             // legal: arrays are covariant
        anything[0] = "TEA";                       // compiles
```

```
  Sku[] assigned to Object[]                    : compiles, runs
  storing a String in it                        : java.lang.ArrayStoreException: java.lang.String
```

`Sku[]` **is** an `Object[]`, so the assignment is legal and the store is checked at run time
instead. Every array store in Java carries that check. C++ has the same hole through `Dog**` and gives you
no diagnostic either, so the array half is familiar. What is new is that two features of one
language disagree. Generics chose compile-time safety; arrays predate generics and could not.

The practical consequence: prefer a `List<T>` to a `T[]` for anything held in a field. You get the
error at the line that is wrong, and you lose the run-time store check you were never using.

## Wildcards, and the rule that settles them in ten seconds

Invariance is strict enough to be inconvenient, and wildcards are the release valve. There are two,
and one question tells you which.

**Look at the parameter and ask which way the values travel.**

- **Out of it, never in** → `? extends`. It produces values, and you read them.
- **In to it, never out** → `? super`. It consumes values, and you write them.
- **Both** → no wildcard. Name the type.
- **A return type** → no wildcard, always. Wildcards belong in parameters, so that a caller reading
  your signature never has to reason about a capture.

That is PECS: producer `extends`, consumer `super`. The JDK is the proof it holds.
`Collection.addAll(Collection<? extends E>)` reads its argument, and
`List.sort(Comparator<? super E>)` is handed something that consumes `E`.

Here is the corpus signature that should have it. `Totals.price` takes the promotions, sorts them by
stage, and takes money off. It never adds one:

```java
    public static long total(List<? extends Promotion> promotions, List<LineItem> lines, long subtotalMinor) {
```

The reference gets away with `List<Promotion>` because `Cart.activePromotions()` happens to build
exactly that. Any other caller pays: a factory holding `List<PercentageCoupon>`, a test with
`List.of(new MultiBuyPromotion("MUG", 4))`, a per-tier cart that keeps its offers typed. Each one
gets the `E6` error above. Each one is then "fixed" with a copy into a new
`ArrayList<Promotion>`, which is a line of code that exists to work around a signature.

`? super` is the other half, and it is rarer, so here is where it earns its place.
`Cart.activePromotions()` builds a list and returns it. Written to fill a list the caller owns:

```java
    public static void collectInto(List<? super Promotion> sink,
                                   List<? extends Promotion> standing,
                                   CodeIndex<? extends Promotion> couponBook) {
        sink.addAll(standing);
        sink.addAll(couponBook.all());
    }
```

```
  collectInto(List<Object>, ...)                : accepted, 3 promotions written
```

A `List<Object>` is accepted, because anything that can hold an `Object` can hold a `Promotion`.
With `List<Promotion>` as the parameter type, a caller keeping a `List<Object>` of everything it has
built cannot use the method.

Two limits worth knowing before you reach for `? extends` everywhere.

**A `? extends` list refuses every write, including putting back what you took out:**

```
E7.java:6: error: incompatible types: PercentageCoupon cannot be converted to CAP#1
        promotions.add(new PercentageCoupon(10));
                       ^
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Promotion from capture of ? extends Promotion
E7.java:10: error: incompatible types: Promotion cannot be converted to CAP#1
        promotions.set(0, promotions.get(0));
                                        ^
```

`CAP#1` is the compiler naming the unknown subtype for you. Learn the word "capture": it appears in
every wildcard error, and it means "there is one specific type here and I do not know which".

**Sorting is not a write, as far as the compiler is concerned.** `promotions.sort(...)` on a
`List<? extends Promotion>` compiles, because `sort` wants a `Comparator<? super E>` and
`Comparator<Promotion>` satisfies it. So `? extends` does not protect the caller's list from being
reordered. `Discounts.inStageOrder` copies for that reason, not because of the wildcard:

```
  the mixed list as it was wired up             : [FIXED_AMOUNT_COUPON, PERCENTAGE_COUPON, ITEM_PROMOTION]
  inStageOrder(...) hands back                  : [ITEM_PROMOTION, PERCENTAGE_COUPON, FIXED_AMOUNT_COUPON]
  the caller's list afterwards                  : [FIXED_AMOUNT_COUPON, PERCENTAGE_COUPON, ITEM_PROMOTION]
```

## No primitive type arguments, and what boxing actually costs

`List<int>` does not exist and never will. A type argument is a reference type, because erasure
writes `Object` and an `int` is not one. So a cart's quantities are `Map<String, Integer>` and its
line totals, if they live in a collection, are a `List<Long>`.

The performance question comes up in interviews, so here is the measurement instead of an opinion.
`worked/` block 8, medians of eleven timed repetitions, four runs of the whole program:

```
  read  5         long[]       1.8 ns   List<Long>       5.3 ns   LongStream      10.2 ns   x2.9
  read  50        long[]       8.5 ns   List<Long>      51.0 ns   LongStream      14.5 ns   x6.0
  read  1000      long[]     235.4 ns   List<Long>     355.9 ns   LongStream     248.3 ns   x1.5
  read  1000000   long[]  246540.0 ns   List<Long> 1122910.0 ns   LongStream  259560.0 ns   x4.6
  build 5         long[]       7.1 ns   List<Long>      24.4 ns                        x3.4
  build 50        long[]      48.4 ns   List<Long>     176.7 ns                        x3.7
  build 1000      long[]    1007.1 ns   List<Long>    3357.7 ns                        x3.3
  heap  1000000   long[]       8.2 bytes/number   List<Long>   28.2 bytes/number   x3.4
```

The absolute numbers move by a fifth between runs and the ratios hold. Over five runs of the whole
program the boxed-to-raw ratio stayed between 6.0 and 6.7 on the 50-line read, and between 3.7 and
4.2 on the 50-line build. The heap ratio never left 3.4 to 3.5.

**Now the honest reading of that, which matters more than the numbers.** A shopping cart has five
to fifty lines. Summing fifty boxed line totals takes 51 nanoseconds against 8.5 for the array, so
boxing the subtotal costs **35 to 43 nanoseconds per `total()` call** across the five runs. A cart
page that renders in one millisecond has a budget of 1,000,000 nanoseconds. This is 0.004% of it. At
LLD scale, in an interview, in production for a cart: it does not matter, and saying it does is the
mistake.

Three things in that table do matter.

**The memory ratio, at scale.** 8.2 bytes against 28.2 per number — a `long` is 8 bytes, a `Long`
is a 16-byte object plus a reference plus the `ArrayList` slot. If you ever hold ten million of
something, that is the row to quote.

**`LongStream` is not free either.** At five and fifty elements it is slower than the plain loop,
10.2 ns and 14.5 ns against 1.8 and 8.5, because a stream pipeline has setup to pay for. It matches
the array loop from about a thousand elements. So "use `IntStream`" is right about boxing and wrong
about speed at small sizes. `int[]`, `long[]` and `IntStream`/`LongStream` are the escape hatches
when a measurement says you need one, and a measurement rarely does at this scale.

**`Integer` can be null, and that is the real cost.** There is no `int` that is absent. This
compiles and reads as if it were safe:

```java
    public int quantityOf(String skuCode) {
        return byCode.get(skuCode);
    }
```

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

The unboxing is invisible in the source, so the throw names a method you did not call.
`getOrDefault(skuCode, 0)` is the fix. Helpful NPE messages, on by default since Java 15, are why
that text names the map call at all.

There is a second edge with the same root. `Integer.valueOf` caches −128 to 127, so `==` on two
boxes is true for small numbers and false for large ones:

```
  quantity 100, boxed twice, compared with == : true
  quantity 200, boxed twice, compared with == : false
  line total 450 as Long, compared with ==    : false
```

J6 is the lesson on `==` against `.equals`. The new part here is that no `new` appears on the line:
the compiler created the object, so nothing in the source looks like an allocation. A quantity
comparison written this way passes every test with a quantity under 128.

## What you lose, and what you get for it

The loss is real and it is worth naming plainly, because dismissing it is the fastest way to look
like you have not used the other language.

**No specialisation.** `template<> class CodeIndex<Sku>` giving the sku index an extra method or a
different `require` has no Java form, full or partial. If `CodeIndex<Sku>` needs to behave
differently, it is a different class or a subclass, and you have re-introduced the duplication the
type parameter removed.

**No non-type parameters.** `std::array<long, 5>` and a `Matrix<3, 4>` whose dimensions are checked
by the compiler cannot be expressed. Sizes are constructor arguments and mistakes are exceptions.

**No compile-time computation.** No `constexpr`, no `if constexpr`, no tag dispatch, no SFINAE and
no concepts. Bounded type parameters are the entire constraint language:
`<T extends Promotion & Comparable<T>>`. A generic Java method cannot decide anything at compile
time; it can only require that `T` has an interface.

**No duck typing.** A template body compiles against whatever the instantiation happens to support,
which is why templates work with types nobody planned for. A Java `<T>` with no bound gives you only
the methods on `Object`. Every capability has to be written into the bound first.

What you get, and each of these is checkable:

**One class file.** `CodeIndex.class` is compiled once for every parameterisation there will ever
be. Two hundred instantiations of a C++ template are two hundred bodies for the linker to fold. Here
there is one, so `getClass() == getClass()` is `true` for `CodeIndex<Sku>` and
`CodeIndex<Promotion>` — nothing else could be returned.

**Error messages you can read.** Every message quoted on this page is at most six lines and names
your file, your line and your type. `CAP#1` is the worst of them, and it is explained by the two
lines under it.

**Separate compilation.** The caller of a generic needs a `.class` file and nothing else. No
header-only libraries, no source shipped, no recompiling the world when a template body changes.

**Errors at the declaration, not the instantiation.** A generic method with a type error in it fails
to compile even if nothing calls it. A template with the same error compiles fine until someone
instantiates it, which is why C++ template bugs arrive from a caller you have never read.

**Interoperation with pre-generic code.** This is the historical reason for erasure, and worth
knowing because it explains why the design is what it is. Java 5 added generics to a language with
millions of lines of `List`-using code, and erasure is what let `List` and `List<String>` be the
same class at run time. The alternative was a language break, and the cost of avoiding it is
everything on this page.

## When a type parameter earns its place, and when an interviewer reads it as over-engineering

STANDARD v1.0 grades design at D3, and level 3 requires that "the seam set is minimal — no
speculative interface with a single implementation and no foreseeable second one". Level 3 penalises
over-abstraction exactly as level 0 penalises none, and the failure tag is
`over-engineered (premature interface)`. A type parameter is a seam, and it counts.

**The threshold, in two conditions that both have to hold.**

1. **Two parameterisations exist in the code you are writing**, or the requirements name the second.
   `CodeIndex<Sku>` and `CodeIndex<Promotion>` are both constructed in `CartFactory`, so this one
   clears it. `Cart<T extends Sku>` has one parameterisation and no second in sight, so it fails.
2. **The body never asks what `T` is.** `CodeIndex.require` does a map lookup and a throw, and works
   the same for both. The moment a method needs `if (t instanceof Sku)`, the type parameter is
   modelling something a subclass or an interface should model, and it cannot do it anyway — see the
   `instanceof` error above.

Miss either and the concrete type is the honest answer. `Catalog` and `CouponBook` with the sku and
coupon types written out are shorter to read, and they are what a reviewer expects.

**Which is worth applying to this lesson's own example.** `CodeIndex<T>` replaces two 30-line
classes with one 40-line class plus a `"sku"` string that exists only to replace a type name. It
clears both conditions and is still arguably the wrong call at this size. The reference solution
ships `Catalog` and `CouponBook` as two files, with different javadoc, different error messages and
no shared abstraction. That choice is defensible and it is the one the corpus made. Extract the
generic when the third registry arrives, or when the duplicated body is long enough that two copies
will drift.
