# From C++ — the same two contracts, and the choice C++ never made you make

`corpus/logger/contract/` has four interfaces: `Appender`, `Formatter`, `Clock`, `LoggerApi`. In C++
all four would be classes with pure virtual methods, and you would never have thought about it
twice. Java splits that one construct into two, and the split is not cosmetic: one of them can hold
a field and the other cannot.

## The pure abstract base class translates exactly

**C++**

```cpp
class Formatter {
public:
    virtual ~Formatter() = default;
    virtual std::string format(const LogRecord& record) const = 0;
};
```

**Java**

```java
public interface Formatter {
    String format(LogRecord record);
}
```

Nothing is lost in that translation. No `virtual`, because every instance method already dispatches
that way. No `public`, because an interface member has no other option. No virtual destructor,
because there is nothing to destroy. The one word that carries information is `interface`, and it
promises what the C++ version only documented: there are no fields here, so no implementor inherits
any state.

`Formatter` in this lesson is copied from the contract unchanged, and it is the case where the
decision takes no thought. Zero state, zero shared code, one method. That is an interface.

## Where it stops being a translation

Now a second requirement. The console destination and the in-memory one both want a running count
of lines taken. In C++ you put the counter in a base class and move on:

```cpp
class CountingAppender : public Appender {
public:
    void write(const std::string& line) final {
        ++written_;
        doWrite(line);
    }
    int written() const { return written_; }

protected:
    virtual void doWrite(const std::string& line) = 0;

private:
    int written_ = 0;
};
```

Java has that shape, and it is an abstract class rather than an interface:

```java
public abstract class CountingAppender implements Appender {

    private int written;

    @Override
    public final void write(String formattedLine) {
        written++;
        doWrite(formattedLine);
    }

    protected abstract void doWrite(String formattedLine);

    public final int written() {
        return written;
    }
}
```

Read the class declaration as the whole answer to the lesson's question. `implements Appender` is
the contract, and `abstract class` is there for one line only: `private int written`. Delete the
counter and this file has no reason to exist.

Two mechanical points that match C++ and one that does not. Instantiating it is refused:

```
M2.java:8: error: CountingAppender is abstract; cannot be instantiated
        CountingAppender c = new CountingAppender();
                             ^
1 error
```

A subclass that forgets the hook is refused, and the message names the method:

```
M3.java:4: error: ListAppender is not abstract and does not override abstract method doWrite(String) in CountingAppender
final class ListAppender extends CountingAppender {
      ^
1 error
```

The one that does not match: `final` on `write` here means what `final` means on a C++ virtual
override, and there is no `override` keyword to pair it with. `@Override` is an annotation the
compiler checks, not a modifier, and it is optional. Leave it off and the code still works;
misspell the method name with it present and you get an error instead of a silent second method.

## One `extends`, many `implements`

Then the JSON destination arrives. It knows what its own lines should look like and where they go,
so it wants to be an `Appender` and a `Formatter` at the same time. `MiniLogger.addAppender` takes
one of each, and the test passes the same object twice.

In C++ this is two base classes and you are done. Here:

```
Three.java:9: error: '{' expected
final class JsonDestination extends CountingAppender, Formatter {
                                                    ^
1 error
```

The caret is on the comma, and the message is about grammar rather than design. There is no
production for a second superclass, so the parser stops looking for a type and starts looking for
the class body. It is the least informative error in this lesson and the one you are most likely to
hit. Read `'{' expected` after a comma as "two supertypes, and one of them is a class".

What compiles is one class and any number of interfaces:

```java
public final class JsonDestination implements Appender, Formatter {
```

`Main` block 6 prints what that produced:

```
  JsonDestination's superclass   : Object, interfaces it implements: 2
```

**Here is the part worth being deliberate about.** Diamond inheritance, virtual bases,
`virtual public Base`, base constructor ordering along two paths, one shared subobject or two: none
of that has a Java equivalent. None of it is a question you can be asked about a Java design
either. A field belongs to exactly one class in the chain, because there is only ever one chain.
The cost is that you get one `extends` and have to choose what to spend it on, and the counter above
spent it.

## `default` methods, and the reason they exist

Curveball 01 asks for destinations that do not block the caller, plus one call that waits for them
to catch up (`corpus/logger/curveballs/01-async-appenders/`). A destination that holds lines needs
a way to be told to hand them over. So `Appender` grows a second method.

Add it the obvious way and count the damage:

```java
public interface Appender {
    void write(String formattedLine);
    void flush();
}
```

Against one class implementing the interface and the two lambdas from `Demo.java`:

```
ConsoleAppender.java:1: error: ConsoleAppender is not abstract and does not override abstract method flush() in Appender
public final class ConsoleAppender implements Appender {
             ^
Demo.java:7: error: incompatible types: Appender is not a functional interface
        Appender console = line -> System.out.println(line);
                           ^
    multiple non-overriding abstract methods found in interface Appender
Demo.java:8: error: incompatible types: Appender is not a functional interface
        Appender audit = auditTrail::add;
                         ^
    multiple non-overriding abstract methods found in interface Appender
3 errors
```

Three errors from adding one method, and the second and third are the interesting pair. A lambda
can only stand in for an interface with exactly one abstract method. Add a second and every lambda
and method reference written against that interface stops compiling, wherever it lives. In the real
corpus that is `Demo.java` and every appender in every hidden test suite.

Now the same method as a `default`:

```java
    default void flush() {
    }
```

All three errors go away, and nothing else changes. A `default` method has a body, so an implementor
that never heard of `flush` inherits a working one. A `default` does not count towards the single
abstract method rule either, so the interface stays usable as a lambda. That is what `default` was
added to the language for in Java 8: `Collection` gained `stream()`, `forEach()` and `removeIf()`
without breaking every `Collection` implementation on earth.

`Main` block 4 shows the result in one line each. One destination overrides `flush` and does work;
the other two inherit the empty body and are unharmed:

```
--- 4. flush(): one override, three inherited no-ops
  before flushAll, buffered has : 1 pending, 0 delivered
  immediate already has         : [[WARN] retrying a flaky request]
  after flushAll, buffered has  : 0 pending, 1 delivered
  delivered                     : [[WARN] retrying a flaky request]
  the lambda survived flushAll  : [[WARN] retrying a flaky request]
```

### The threshold for a `default`

Two reasons are good, and there is no third.

1. **Backward compatibility.** The interface is published, implementors exist that you do not
   control, and the new method has an honest do-nothing or derive-it-from-the-others answer.
   `flush()` qualifies: a destination holding nothing genuinely has nothing to flush.
2. **A convenience overload over the abstract method.** `writeAll(List<String>)` is a loop calling
   `write`, and it belongs on the interface because a lambda implementor cannot declare it. The
   whole body is below, and note that it touches no field.

```java
    default void writeAll(List<String> lines) {
        for (String line : lines) {
            write(line);
        }
    }
```

`Main` block 5 runs that on a method reference, which declared nothing at all:

```
  a one-expression Appender took : 3 lines
  in order                       : [connected, retrying, gateway timed out]
```

The threshold in the other direction: **a `default` is not a place to put shared implementation
that needs state.** It cannot hold any, and the next section is why. When two implementations need
to share a field, the thing you want is the abstract class.

Two modifiers you might reach for are refused, both because a `default` is not a class method:

```
M1.java:3: error: modifier protected not allowed here
    protected default void flush() { }
                           ^
M1.java:4: error: modifier final not allowed here
    final default void reset() { }
                       ^
2 errors
```

So a `default` is always public and always overridable. You cannot publish one an implementor is
forbidden to replace, which is a real difference from a non-virtual method on a C++ base class.

## A field in an interface is not the field you meant

Try to give `writeAll` a counter:

```java
interface Appender {
    int written = 0;
    void write(String line);
    default void writeAll(List<String> lines) {
        for (String line : lines) {
            written++;
            write(line);
        }
    }
}
```

```
F1.java:6: error: cannot assign a value to static final variable written
            written++;
            ^
1 error
```

Read the message rather than the fix. Neither `static` nor `final` appears in the declaration, and
the compiler used both words. **Every field declared in an interface is implicitly
`public static final`.** One value, shared by the interface and every implementor, fixed at class
initialisation.

The two adjacent mistakes, in case you meet them first:

```
F2.java:2: error: modifier private not allowed here
    private int written = 0;
                ^
1 error
```

```
F3.java:2: error: = expected
    int written;
               ^
1 error
```

A constant with no value has nowhere to get one, so leaving the initialiser off is a syntax error
rather than a field. `Main` block 6 prints what a legitimate interface constant becomes:

```
  declared  int MAX_LINE_CHARS = 4096;
  really is public static final int
  declared  void write(String formattedLine);
  really is public abstract
  declared  default void flush() { }
  really is public, isDefault=true
  Appender: 1 field(s) declared, 0 of them per-instance
  CountingAppender: 1 field(s) declared, 1 of them per-instance
```

Those last two lines are the lesson in six words. The interface declares a field and has none per
instance; the abstract class declares one and has one.

**Where an interface constant is defensible, and where it is not.** `MAX_LINE_CHARS` is a bound
every destination is held to and none of them may change, so publishing it on the contract is
honest, and `MiniLogger.offer` reads it. Anything a caller might want to configure per instance is
the wrong shape for this, because there is exactly one of it for the whole program. Enums and
constructor parameters are where the configurable version goes, which is J10 and J4.

## The one diamond that survived

Two interfaces, both with a `default` of the same signature, one class implementing both:

```java
interface Appender  { default String describe() { return "appender";  } }
interface Formatter { default String describe() { return "formatter"; } }

final class JsonDestination implements Appender, Formatter { … }
```

```
D1.java:11: error: types Appender and Formatter are incompatible;
final class JsonDestination implements Appender, Formatter {
      ^
  class JsonDestination inherits unrelated defaults for describe() from types Appender and Formatter
1 error
```

Bodies can now be inherited from two places, so ambiguity is back. Java's answer is that there is no
resolution rule at all: it is a compile error, and you write the answer yourself.

```java
    @Override
    public String describe() {
        return Appender.super.describe() + "+" + Formatter.super.describe();
    }
```

`Appender.super.describe()` is the one syntax in this lesson with no C++ counterpart. Compare it
with virtual inheritance: no dominance rules, no most-derived-override table, no silent winner. Two
bodies means you name which one, in a method you had to write.

This is also an argument for keeping `default` methods rare. Every one you add is a body somebody
can collide with, and a single-abstract-method interface can never collide with anything.

## When the answer really is an abstract class

The shape when you want a contract and shared state at once is both, in two files:

```java
public interface Appender { void write(String formattedLine); }

public abstract class CountingAppender implements Appender { private int written; … }

public final class ListAppender extends CountingAppender { … }
public final class BufferedAppender implements Appender { … }
```

Note `BufferedAppender` in that list. It implements the interface directly, shares nothing with the
counter, and holds two fields of its own. A hierarchy with only the abstract class in it would have
forced it under a base class that had nothing to give it.

**How often this comes up, counted.** Across the 20 corpus problems there are 87 interfaces and 6
abstract classes. Five of the six are exception bases, where the shared state is the message and
cause, and the set of subclasses is closed. The remaining one is
`corpus/food-ordering/reference/src/OrderStage.java`, a template method with two `final` fields and
a `final enter()` that calls down into two hooks. Its own javadoc argues the case, and it is worth
reading as the shape of a justified abstract class.

So the honest summary is that most LLD problems never need one. Two conditions have to hold
together. A second implementation already exists and shares a **field** with the first. And the
sequence around that field must not be reorderable by a subclass. Shared code with no shared state
is a static helper method or a `default`, and shared state with one implementation is one class.

## The delta table

| C++ | Java |
|---|---|
| class with only pure virtual methods | `interface`, and the translation loses nothing |
| pure virtual method | ordinary interface method; `abstract` and `public` are implicit |
| base class with a data member | `abstract class`; an interface cannot hold instance state |
| multiple inheritance of implementation | one `extends`, and it is spent as soon as you use it |
| multiple inheritance of pure interfaces | any number of `implements`, no restriction |
| virtual inheritance, virtual bases | no equivalent, and no question you can be asked |
| diamond ambiguity on data members | impossible; a field belongs to one class in one chain |
| diamond ambiguity on behaviour | possible, but only between two `default`s, and it is an error |
| `Base::method()` to disambiguate | `Appender.super.describe()`, interfaces only |
| adding a method to a published base class | adding an abstract method breaks every implementor; a `default` does not |
| non-virtual method on a base class | no interface equivalent; `final default` is refused |
| `static const` member in a base | any interface field, implicitly `public static final` |
| `virtual` on every overridable method | nothing; every instance method already dispatches virtually |
| `override` keyword, checked | `@Override` annotation, optional, checked when present |
| `final` on an override | `final` on the method, same meaning |
| a lambda converts to any callable | a lambda needs an interface with exactly one abstract method |
| protected data member | legal, and `protected` on an interface member is refused |

## Two habits that will cost you time today

**Reaching for an abstract class because the C++ base class was free.** In C++ a base class costs
nothing you will feel, because you can always add another. Here the first abstract class in a
hierarchy spends the only `extends` every subclass has, and you find that out later, when a
subclass turns out to need a second role. `Appender` was a one-method interface for exactly this
reason, and it is why `03-rolling-appender` costs the reference zero lines.

**Writing an interface with one implementation because it looks like good separation.** The corpus
justifies `Appender` with a real second implementation and a third in a curveball. An interface
with one implementor and no second in sight is what STANDARD v1.0 penalises under the
`over-engineered` tag, and B1's `when-not.md` is where that argument is made in full.

## What this lesson deliberately leaves out

There is a third option for a closed set of variants: a `sealed interface` with `record`
implementations, which lets the compiler check that a `switch` covered every case. That is the
modern answer when you know all the implementors and want to keep knowing. It is J11, along with
`record`, `var` and switch patterns, and choosing it needs those first.
