Why One-Method Interfaces Are So Common They Got Their Own Shorthand
How lambda expressions are secretly just one-method interfaces in disguise, and a simple test for deciding whether a behavior belongs in a class or an interface.
The previous post landed on a rule: interfaces should stay light, ideally down to a single method. That rule turns out to be common enough, across real languages, to have earned its own shortcut.
What a Lambda Expression Actually Is
Take a small piece of code most people have seen in some language or another:
auto doSomething = [](int x, int y) { return x + y; };This is just a one-method interface, without the extra work of naming the interface or the class that implements it. Behind the scenes, the language still creates both, an interface with one method, and a class that implements it. The short syntax is just a shortcut for a pattern that already existed.
Write that same thing out the long way, by hand, and this is what's actually there underneath:
struct Doer {
virtual int compute(int x, int y) = 0;
virtual ~Doer() = default;
};
struct DoSomething : Doer {
int compute(int x, int y) override { return x + y; }
};
Doer* doSomething = new DoSomething();One interface, one class, one object, just to hold a single line of logic. The lambda from before does exactly this, minus all the typing.
One-method interfaces are common enough in real code that many languages built a dedicated shorthand for them. That shorthand goes by different names: lambda expressions in Java and Kotlin, arrow functions in JavaScript and TypeScript. But the shape underneath is the same everywhere, one method, wrapped in less syntax.
This pattern has a name too: a functional interface, an interface with exactly one method. It's worth recognizing on sight, because it shows up constantly in standard libraries, not as a special case.
Functional Interfaces Are Already Everywhere
Two examples make this concrete. Making a class runnable on its own thread, in most languages, means implementing an interface with exactly one method: run(). Making a list of objects sortable means implementing an interface with exactly one method: compare().
Both of these are functional interfaces, quietly doing their job long before anyone called them that. The pattern from the last few posts, Flyable with just fly(), Eater with just eat(), isn't something special built for birds. It's the same shape every functional interface in a standard library already uses.
Why Bother With an Interface for Just One Method?
A fair question: if an interface only ever has one method, why not just put that method straight on the class and skip the interface entirely?
Because the interface isn't there for the method. It's there for the type.
Without Flyable as a real type, there's no way to write Flyable[] and mean "every object that can fly." There's no way to declare a variable that only cares about flying and nothing else. The method itself could live directly on each bird class just fine.
What can't exist without the interface is one data type that spans every class implementing it. That's the exact problem this series has been solving since the penguin first showed up.
A Test for Where a Behavior Belongs
One more thing to get right: not every behavior needs its own interface. walk() has stayed a plain method on Bird this whole time, never split out, and that was the right call.
The split isn't about how many methods an interface should hold. It's about whether a behavior can be safely assumed for every instance, or whether it genuinely varies.
Flyable and Eater were already separate, and staying that way was a decision, not a code change. The LSP checkpoint is still the current full code.Keep reading