Skip to main content

Realization

Realization is the odd one out on this page: every other relationship connects two concrete classes, but realization connects a class to an interface. It's the "implements" arrow - a Circle realizing a Shape, an Invoice realizing a Payable

  • and it's worth a page of its own because it's the one relationship that's a binding contract rather than a casual link, and the one most often confused with its closest neighbour: plain old inheritance.

Definition: fulfilling a contract, not borrowing an implementation

A class realizes an interface by providing a concrete method body for every method the interface declares. The interface states what must exist; the class decides how. Nothing about the interface's (nonexistent) implementation gets inherited - there's nothing to inherit, since an interface has no method bodies and no state to begin with:

interface Payable {
double amountDue();
}
 
class Invoice implements Payable {
private List<LineItem> lineItems;
 
public double amountDue() {
return lineItems.stream().mapToDouble(LineItem::total).sum();
}
}

Invoice promised to have an amountDue() method that returns a double, and it wrote every line of that method itself. Payable supplied the shape of the promise; Invoice supplied all of the substance.

UML notation

Realization draws as a dashed line with a hollow (unfilled) triangle arrowhead pointing at the interface - the same triangle shape used for generalization (inheritance), but on a dashed line instead of a solid one. That's the entire visual difference, and it's exactly backwards from what people expect: the stronger-looking solid line is the weaker binding (see below), while the lighter-looking dashed line is the one that breaks compilation if you get it wrong.

«interface»Payable+ amountDue(): double«abstract»BillingDocument- id: string+ *describe(): stringInvoice- lineItems: List<LineItem>+ amountDue(): double+ describe(): string
implementsextends
Invoice extends BillingDocument (solid triangle, inherits id and shares describe()'s default) and implements Payable (dashed triangle, writes its own amountDue() from scratch).

One class can realize several interfaces at once - a SubscriptionPlan could just as easily implement Payable alongside Invoice, each providing its own unrelated amountDue() body, and the diagram would simply show two dashed arrows leaving two different classes toward the same interface box.

Multiplicity - not applicable here either

Like dependency, realization isn't counting instances of anything - a class either realizes an interface or it doesn't, and that's a static, compile-time fact about the class's declaration, not a runtime count of connected objects. You won't see multiplicity written on a realization arrow for the same reason you won't see it on a generalization arrow: both describe a class's type, not a collection of related objects.

Directionality

Realization is always unidirectional, from the implementing class to the interface - Invoice knows about Payable, but Payable has no idea Invoice exists, let alone how many classes realize it. This mirrors generalization exactly: a subclass knows its superclass, never the reverse.

Telling realization apart from generalization (extends)

This is the comparison that actually matters, because the two relationships look similar (both use a triangle arrowhead) and both often show up between a class and something more abstract above it. The test: does the subtype inherit any concrete implementation or state, or only an obligation to have certain methods?

  • Generalization (extends): Invoice extends BillingDocument inherits BillingDocument's id field and whatever concrete method bodies BillingDocument already wrote (its describe() default, if it has one). The subclass gets real, reusable code for free.
  • Realization (implements): Invoice implements Payable inherits nothing but an obligation - Payable has no fields and no method bodies to hand down. Invoice writes amountDue() completely from scratch; the interface only guaranteed that a method with that name and signature would exist somewhere.

Put differently: generalization shares code down a solid line because the subclass is a specialization of its parent. Realization shares nothing but a promise down a dashed line because the implementing class behaves as the interface requires, without being any kind of specialization of it.

Telling realization apart from dependency

A class that merely takes an interface as a parameter type has a dependency on it, not a realization - PriceEngine.apply(Payable item) depends on Payable without promising to ever implement it. Realization is reserved specifically for the class on the other side of that relationship: the one that wrote the method bodies. The practical difference in stakes is real: drop a dependency and, at worst, you delete a parameter and adjust a call site. Drop a method a realized interface requires and the class does not compile - a dependency can be walked away from casually, a realization cannot.

Worked example

interface Payable {
double amountDue();
}
 
class Invoice implements Payable {
private List<LineItem> lineItems;
 
public double amountDue() {
return lineItems.stream().mapToDouble(LineItem::total).sum();
}
}
 
class SubscriptionPlan implements Payable {
private double monthlyFee;
private int monthsRemaining;
 
public double amountDue() {
return monthlyFee * monthsRemaining;
}
}
 
// Anything holding a Payable can call amountDue() without caring which
// realization it received - that's the entire payoff of the contract.
double total(List<Payable> bills) {
return bills.stream().mapToDouble(Payable::amountDue).sum();
}

total never mentions Invoice or SubscriptionPlan by name. Both classes realized Payable independently, wrote completely different math inside amountDue(), and are now interchangeable everywhere the interface is the declared type - which is the whole reason to reach for realization over a shared abstract base class when the implementations genuinely have nothing in common but that one method's signature.

Common mistakes

  • Drawing a solid line for "implements." The line must be dashed - a solid triangle line is reserved for generalization, and mixing them up tells the reader the wrong story about how much code is actually shared.
  • Using an abstract class purely to fake an interface, then treating it as realization. If BillingDocument had zero fields and zero method bodies - only abstract method signatures - it's functionally acting like an interface, and calling the relationship "generalization" instead of "realization" is a modeling choice that hides how little is actually being inherited. Prefer an actual interface when there's no state or default behavior to share.
  • Fulfilling the contract with a body that throws. A method that satisfies the compiler with throw new UnsupportedOperationException() technically realizes the interface, but breaks the promise realization is supposed to represent for any caller that trusts the interface's contract - a Liskov substitution violation wearing a realization's clothing.

Try it yourself: model a Comparable interface with one method, compareTo(other): int, realized by a Money class that compares by amount. Then ask: if Money also extended an abstract Currency base class for shared formatting logic, which of the two relationships on that diagram would break compilation if you deleted the method body, and which would just mean losing some free formatting code?

Check yourself

Question 1 of 3

What UML notation distinguishes realization from generalization, given that both use a triangle arrowhead?