Concepts / Defining Functional Interfaces

Defining Functional Interfaces

A functional interface is a Java interface with a single abstract method, and Runnable is a standard example with one method, run.

  • Programming

Predicting Output Before the Trace

Many Java programs need a small piece of behavior passed around as a value. Java can express that behavior with a named class, but for some interfaces a shorter form called a lambda expression is enough. Before learning the rules, look at the three lines below and predict what the program prints. Focus on the variable called greeting and the method call on its last line. Do not worry yet about the arrow symbol.

What do you think happens?

What do you think this program prints?

  • Nothing, because the lambda is never called

  • Hello from a lambda

  • A compile error because greeting is not a class

Reveal answer
Answer:

Hello from a lambda

The variable greeting has type Runnable, and it holds a lambda whose body prints the text. Calling run on greeting invokes that body, so the text is printed. The lambda is called, so nothing is skipped.

java
Output
Hello from a lambda
Notes on the code
  • Runnable is the functional interface type of the variable greeting
  • The empty parentheses are the parameter list of run, which has no parameters
  • The text after the arrow is the body that runs when run is called

What Makes an Interface Functional

Definition

A functional interface is a Java interface with exactly one abstract method. That single method defines the shape of a piece of behavior: what inputs it takes, what it returns, and what it is called. The interface does not say how the behavior is carried out. It only provides a template that some implementation must fill in.

Runnable is the standard example. Its declaration contains exactly one method, named run, which takes no parameters and returns nothing. Because the interface has a single method to implement, a lambda expression can stand in for a complete implementation of it. The word functional refers to this one-method shape: the interface describes one piece of behavior and nothing else.

A functional interface is also a Java file. It is defined in a .java file just as a class is, and its file name matches the name of the interface. That file holds a specification for exactly one subroutine. If you need to describe two different subroutines, you write two interfaces, not one interface with two methods.

Tracing a Runnable Program

Now trace the program from the start. The program creates a Runnable variable named greeting and assigns it a lambda. Nothing is printed at that point, because assigning the lambda only stores the behavior. The text is printed later, when the program calls run on greeting. The diagram shows which code runs next after that call.

main methodgreeting variablelambda bodycalls run()invokes the lambdaprints the greeting text
When the program calls run(), which code runs next, and which line actually produces the printed output?
  1. 1
    Line 1 creates the variable greeting with type Runnable and stores the lambda in it. No printing happens here.
  2. 2
    Line 2 calls run on greeting. Java looks at the value held by greeting, which is the lambda.
  3. 3
    The lambda body runs. The line System.out.println inside the body is the one that produces the printed output.
  4. 4
    The output appears on the screen as Hello from a lambda. The variable itself never prints anything.

Lambdas as Values of an Interface Type

Read the lambda again. The empty parentheses on the left stand for the parameter list of run, which has no parameters. The body on the right is the code that runs when run is called. The lambda therefore carries exactly the information the interface template leaves open: the implementation of the one method.

Runnablegreetinglambda valuedeclared with typeholds
When a variable is declared with the interface type, what object is stored in it, and how does that value relate to the interface?

The diagram shows the two halves of the relationship. The interface declares the method and gives it a type. The lambda is the value that supplies the method's behavior. The variable greeting connects them: its type is Runnable, and the value it holds is the lambda.

Because the name of a functional interface is a type, in the same way that String and double are types, you can use it in three places. You can declare a variable with it, declare a parameter with it, and state it as a method's return type. In the source's second example, a method named makeTask has Runnable as its return type. The lambda it returns is a value of that type, so the caller can invoke run on it just as it would on the variable in the first half. The type name is what connects the lambda to the method it implements.

java
Output
Task finished
Notes on the code
  • The return type Runnable is the functional interface name used as a type
  • The lambda is returned as a value of type Runnable
  • The caller uses run on the returned value without knowing how it was written

Reading a Declaration Line by Line

The source gives a first example that is a whole functional interface on one line. It is saved in a file named FunctionR2R.java, which matches the name of the interface. Read the declaration from left to right. The name of the interface is FunctionR2R. Inside the braces is one line that names a subroutine, valueAt. That line lists one parameter, of type double and named x. The word double before valueAt is the return type.

java
Notes on the code
  • FunctionR2R is the interface name
  • double before valueAt is the return type
  • valueAt is the method name
  • (double x) is the parameter list: one double named x
FunctionR2Rinterface namedoublereturn typevalueAtmethod name(double x)parameter list
In a declaration like FunctionR2R's, which part is the interface name, which is the method name, which is the parameter list, and which is the return type?

So the entire declaration says: there is a subroutine called valueAt, it takes one double, and it returns a double. Those three facts, the name, the parameters, and the return type, are the specification. The parameter name x is written in the declaration but is not really part of the specification, so when you check a use you should compare the number of parameters and their types.

What do you think happens?

Before checking the file, predict: does this file contain any code that computes the answer to valueAt?

  • Yes, the computation is inside the braces

  • No, the file only describes the shape of the call

Reveal answer
Answer:

No, the file only describes the shape of the call

The declaration ends after the semicolon. It names the subroutine, its parameter, and its return type. It has no method body and no implementation. If computation were present, it would appear inside the braces, and the braces hold only the single declaration line.

The source gives a second example with a different shape. It describes a subroutine that returns nothing and takes two parameters, an array of strings and an int. The two declarations have the same structure even though their contents differ. Each names a single subroutine and then states its parameters and its return type. A return type of void is still a return type. It tells the caller that no value comes back.

Specification factFunctionR2R valueAtSecond example
Parametersone double, named xan array of strings and an int
Return typedoublevoid, so no value is returned

The shape stays the same across both declarations; only the values filled into it change.

Finding Where a Call Breaks the Specification

Once you know the three facts, you can debug a broken use of a subroutine by asking where the use departs from the specification. You do not need to guess. Compare the call against the declaration line by line, starting with the name, then the number and types of parameters, then the return type.

  • Common mistake

    Passing the wrong number of arguments to a subroutine

    What it looks like

    valueAt(2.0, 3.0) when the declaration takes one double

    Why it fails

    The specification says there is one double parameter. The call supplies two values, so the parameter count disagrees with the declaration.

    Do this instead

    Pass exactly one double, for example valueAt(2.0), or change the specification by writing a new interface if the subroutine should really take two values.

  • Common mistake

    Expecting a value back from a subroutine with a void return type

    What it looks like
    double result = task.run(); where run returns nothing
    Why it fails

    A void return type tells the caller that no value comes back, so there is nothing to store in result.

    Do this instead

    Call run as a statement, such as task.run();, and do not assign its result.

  • Common mistake

    Looking for the computation of a subroutine inside its interface file

    What it looks like

    Searching FunctionR2R.java for the arithmetic that valueAt performs

    Why it fails

    A functional interface records only the specification. The file has no method body and no implementation.

    Do this instead

    Look for the implementation in the lambda or class that supplies the method's behavior.

  • Common mistake

    Treating the interface name as if it were a class with fields

Practice Locating the Mismatch

MEDIUM

Using the declaration interface FunctionR2R { double valueAt(double x); }, a learner writes the call valueAt(2.0, 3.0) and assigns its result to an int variable named total. Identify every point where this use disagrees with the specification. Name the fact (name, parameters, or return type) for each disagreement, and state the single correction needed for each one.

Hints
  • Compare the number of values in the call with the number of parameters in (double x).
  • Compare the type of the value that comes back, double, with the type of total, int.
  • Check the method name in the call against valueAt.
EASY

Write a lambda that supplies the value of a Runnable variable named report. Its body should print the text Report ready. Then call run on report and state which line produces the printed output.

Hints
  • A Runnable lambda takes no parameters, so the parentheses are empty.
  • The output comes from the body of the lambda, not from the assignment.

Recap of the Specification

  • A functional interface is a Java interface with exactly one abstract method, and it is defined in a .java file just as a class is.
  • The specification records three facts about one subroutine: its name, its parameters, and its return type.
  • A lambda expression supplies the body of the single abstract method, and a variable or return value typed with the interface name can hold that lambda.
  • The interface file contains only the declaration, not the implementation, so mismatches are found by comparing a call against the declaration fact by fact.

Key Takeaways

  • A functional interface is a Java interface with exactly one abstract method, such as Runnable with its single method run.
  • The specification of one subroutine has three facts: its name, its parameters, and its return type.
  • A lambda expression supplies the body of that one method, and the interface name works as a type for variables, parameters, and return values.
  • The interface file holds only the declaration, so a mismatch is located by comparing a call against the declaration line by line.