Concepts / Functional Interfaces and Lambda Expressions

Functional Interfaces and Lambda Expressions

A default method is declared inside an interface with the modifier default and carries a working implementation.

  • Programming

Why Interfaces Used to Be Limited

Until Java 8, an interface could only declare methods without bodies. Any class that implemented the interface was required to supply a body for every one of them. An interface was therefore a promise of behavior, never a source of working code. This article looks at the feature that changed that rule: the default method.

A default method is a method declared inside an interface that comes with its own implementation. Because the interface now carries working code, a class that implements the interface can receive that code without writing it. The rest of this article explains how such methods are declared, inherited, overridden, and combined, and it begins with a prediction you can test for yourself.

A Prediction About readLine()

Consider an interface named Readable. It declares an abstract method readChar() and a default method readLine(). A class named FileSource implements Readable, provides a body for readChar(), and says nothing about readLine(). Before reading on, decide what happens when a caller invokes readLine() on a FileSource object.

java

What do you think happens?

What happens when you call readLine() on a new FileSource object?

  • It fails to compile because FileSource does not declare readLine()

  • The default body in Readable runs, and it calls FileSource's readChar()

  • It returns an empty string because the interface has no fields

Reveal answer
Answer:

The default body in Readable runs, and it calls FileSource's readChar().

FileSource inherits readLine() from Readable without writing it. The inherited body calls readChar(), and because FileSource supplies that method, the call reaches FileSource's own code. Each call to readChar() returns the next character, so new FileSource().readLine() returns "[ab]".

The prediction works out this way because of the central idea of the topic. The interface carries working code, not just a promise of code. The class did not write readLine(), yet the method is still callable on its objects, and its body runs.

Recognizing the default Modifier

A default method is written in the body of an interface and follows a fixed pattern. The word default must appear in the declaration. That keyword is the element that separates a default method from an ordinary abstract method. Without it, a method inside an interface is abstract and has no body, so a method that has a body but lacks default is not a valid interface method.

java
Notes on the code
  • default: marks the method as a default method with a body
  • public: allowed but optional, because everything in an interface is automatically public
  • String: the return type, following the same rules as any other method
  • readLine(): the method name
  • { ... }: the block that holds the implementation

The access modifier public can be dropped in real code. The return type, the method name, and the parameter list follow the same conventions as any other method. What makes the method a default method is the default keyword together with the braces that enclose its body.

Functional Interfaces and Default Methods

Definition

A functional interface is an interface that specifies a single abstract method. It may also include default methods alongside that one abstract method.

The abstract method is still the only one a lambda or implementing class must supply, because the default methods already have bodies. A functional interface can therefore offer helper behavior built on top of its single required method without making the implementer write that helper. This is why default methods matter to the functional interface idea: they add capability without adding to the required work.

Inherited or Overridden: Which Body Runs?

A class that implements an interface with default methods has two possible relationships to each default method. It can leave the method alone, in which case it inherits the interface's version. Or it can write its own body, which overrides the interface's version. The inheritance works the same way a subclass receives an ordinary method from a parent class. When the class's own version exists, it takes priority.

Call readLine() onthe objectDoes the classoverride readLine()?Run the class's ownreadLine() bodyRun the interfacedefault bodyBody calls readChar()abstract in the interfaceClass's readChar()runsyesno
When a caller invokes readLine() on an object, the question is whether the object's class supplies its own readLine(). If it does, that body runs. If it does not, the interface's default body runs, and its call to readChar() reaches the class's version.

Follow the flowchart with the FileSource class. FileSource does not write readLine(), so the decision branch labeled no is taken. The interface's body runs, and when it reaches readChar(), it uses the version FileSource supplied. Now change the scenario: if a class supplies its own readLine(), the decision branch labeled yes is taken, the interface's default body is skipped entirely, and readChar() is never called by that path.

java
Output
new ReverseSource().readLine() returns "custom"; readChar() is not called by readLine()

Calling Abstract Methods from a Default Body

A default method is allowed to call the abstract methods declared in its own interface. In the Readable example, readLine() calls readChar(), which has no body in the interface. That call is valid because every concrete class that implements Readable must supply readChar(), so the call always reaches real code at run time.

CallerFileSource objectReadable defaultreadLine()FileSource readChar()1. readLine()2. inherited body runs3. readChar()4. returns a character5. returns the string
The caller starts readLine() on a FileSource object. The inherited default body runs and calls readChar(). Because FileSource supplies readChar(), that call executes FileSource's code, and the result flows back up to the caller.

Read the sequence top to bottom. The inherited default body is the only code that calls readChar(), yet the code that answers that call belongs to FileSource. The interface can therefore describe a procedure in terms of an operation it does not implement, and each class supplies the operation in its own way.

What a Default Method Cannot Reach

A default method cannot refer to instance variables. Interfaces do not hold instance variables at all, so there is no per-object state a default method could read or change. A regular class method, by contrast, can see the fields of the object it belongs to. The comparison below shows the difference.

FeatureRegular class methodDefault method in an interface
Where it is declaredInside a classInside an interface
Can read instance variablesYes, the fields of its objectNo, interfaces hold no instance variables
Can call abstract methodsYes, when a superclass declares themYes, abstract methods of its own interface
What it depends on for stateFields stored by the objectOnly methods the implementing class is required to supply

Default methods gain behavior but no stored state

Mixing Behavior from Several Interfaces

Other programming languages offer a feature called a mixin, which lets a class take in functionality from another source. Default methods give Java a similar capability. Because a class can implement several interfaces, it can mix in functionality from several different sources. The result is not multiple inheritance in the full sense, since interfaces still store no instance variables, but a class can combine behaviors that were defined separately.

java
Output
LOG: start
saving
Loggabledefault log()ReportSavabledefault save()implementsimplements
The Report class receives a log() behavior from Loggable and a save() behavior from Savable. Neither interface refers to the other, and Report writes neither method body.

The Report class did not have to write either method body. Each interface supplies one behavior, and the class combines them through its implements clause. Writing its own body for log() or save() would override the corresponding default, following the same priority rule covered earlier. The source pack does not cover how Java resolves a conflict in which two interfaces supply default methods with the same signature, so that case should be studied separately.

Mistakes With Default Methods

  • Common mistake

    Writing a method body in an interface without the default keyword

    What it looks like
    interface Readable { String readLine() { return "x"; } }
    Why it fails

    Without default, a method inside an interface is abstract and has no body.

    Do this instead
    interface Readable { default String readLine() { return "x"; } }
  • Common mistake

    Expecting a default body to read a field of the implementing class

    What it looks like

    default String readLine() { return text; } where text is a field of FileSource

    Why it fails

    Interfaces hold no instance variables, so a default method has no per-object state to read.

    Do this instead

    Call a method that the implementing class supplies, such as readChar(), instead of reading a field.

  • Common mistake

    Assuming a class must write every default method it inherits

    What it looks like
    Writing a readLine() in FileSource just to make it work
    Why it fails

    Default methods already carry a body, so a class that leaves them alone inherits working behavior.

    Do this instead

    Write an override only when the class needs different behavior.

  • Common mistake

    Assuming an overriding body is also called by the interface: the interface's default body is skipped when the class has its own version.

Practice: Predict Before You Run

MEDIUM

Using the Readable interface and the ReverseSource class above, what does new ReverseSource().readLine() return, and is readChar() called during that call? Write your prediction first, then check it against the explanation in the section on inheritance and overriding.

Hints
  • Ask first whether ReverseSource supplies its own readLine().
  • If it does, the interface's default body is not run.
  • Remember that the return value comes from the body that runs.

Key Takeaways

  • A default method is declared inside an interface with the modifier default and carries a working implementation; without default, an interface method has no body.
  • A class that implements an interface inherits each default method unless it writes its own body, and the class's version takes priority when present.
  • A default method can call abstract methods of its interface, because every concrete implementing class must supply them, but it cannot use instance variables because interfaces hold none.
  • A class implementing several interfaces can mix in default behavior from each, combining separately defined behaviors without gaining stored state.
  • A functional interface has one abstract method and may also include default methods, which add helper behavior without adding to the implementer's required work.