Interfaces in Java
A functional interface is a Java file that holds a specification for exactly one subroutine, and it is defined in a .java file just as a class is.
Calling a Subroutine You Did Not Write
Imagine that someone else has written a subroutine and you want to call it. Before you write the call, you have to answer a few questions. What is the subroutine called? How many values does it expect you to pass in, and what type is each one? What type of value does it send back? If any of these answers is wrong, the call cannot be made correctly. The information that answers these questions is the subroutine's specification.
A functional interface is one way to write that specification down in a Java file. In this article you will learn what a functional interface records, how to read one, how to predict what its file contains, and how to locate the exact point where a call disagrees with the specification. Keep the calling picture in mind as you read: the question we are working toward is what a functional interface contains, and how you can tell from the code whether a use of it matches what it promises.
What a Specification Records
A functional interface is a Java file that holds a specification for exactly one subroutine. It is defined in a .java file just as a class is, and the file is named after the interface, as with any Java class.
The specification records three facts about that one subroutine. The first is its name. The second is its parameters, meaning how many there are and what type each one has. The third is its return type, which tells you what kind of value comes back to the caller.
The word one matters. A single functional interface describes a single subroutine. If you need to describe two different subroutines, you write two interfaces.
Reading the valueAt Declaration
The source's first example is a whole functional interface squeezed onto one line. It is saved in a file named FunctionR2R.java, which matches the name of the interface. The declaration is written out here as a single line inside the interface's braces:
Read it from left to right. The name of the interface is FunctionR2R. Inside the braces there is one line that names a subroutine, valueAt. That line lists one parameter, which has type double and is called x. The word double before valueAt is the return type. So the entire declaration says: there is a subroutine called valueAt, it takes one double, and it returns a double. The diagram below labels each part of the line.
What do you think happens?
Before you check the declaration, predict: does the file contain code that computes how valueAt finds its answer, or only the shape of the call?
Reveal answer
It contains only the shape of the call, with no computation
The declaration names the subroutine, lists its parameters, and gives its return type. If the file contained computation, you would expect to find it inside the braces. The braces hold only the one declaration line, and nothing computes the answer there.
Tracing the Declaration Line by Line
Once you can name each part of a declaration, you can trace the whole specification by walking through it in order. Work through the valueAt declaration this way, and use the result to decide what a caller must supply and what the caller will receive.
Reading what a caller must supply
Using only the declaration interface FunctionR2R { double valueAt(double x); }, state what a caller must pass in and what the caller gets back.
- 1Find the name
The subroutine is called
valueAt, the word directly before the parenthesis. - 2Count the parameters
Inside the parentheses there is exactly one entry, so the subroutine takes one parameter.
- 3Check the parameter type
That one parameter is declared as double, so the caller must pass one double value.
- 4Find the return type
The word double before
valueAtis the return type, so the caller receives one double value back.
A caller must pass exactly one double and will receive a double back. The name used for the parameter, x, is not something the caller has to match.
A Second Shape: No Return Value
The source gives a second example with a different shape. It describes a subroutine that does not return anything and takes two parameters, an array of strings and an int. The source does not give this declaration a name, so the example below uses a placeholder name, printLines, to show the structure.
The two declarations have the same structure even though their contents differ. Each one 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. The shape never changes; only the values placed into it do. Here the parameters are a String array and an int, and the return type is void.
Finding Where a Call Diverges
Once you know the three facts, you can debug a broken use of a subroutine by asking where the use departs from the specification. Suppose some code calls valueAt with two numbers instead of one. The specification says there is one double parameter, so the mismatch is in the number of parameters. You do not need to guess; you compare the call against the declaration line by line, fact by fact.
Common mistake Treating the parameter name as part of the contract
What it looks likeA call supplies a value to a parameter it believes is called something else.
Why it failsThe parameter name is written in the declaration but is not really part of the specification.
Do this insteadCompare the number of parameters and their types when checking a use.
Common mistake Expecting computation inside a functional interface
What it looks likeLooking inside the braces of the valueAt declaration for the arithmetic that produces its answer.
Why it failsThe file holds a specification only, and the declaration states the name, parameters, and return type without computing anything.
Do this insteadExpect the file to contain the shape of the subroutine, not the work it does.
Common mistake Checking only the name of the subroutine
What it looks likeConfirming that a call to valueAt is spelled correctly but ignoring that it passes two numbers.
Why it failsThe specification has three facts, and a call can match on the name while still disagreeing on parameters or return type.
Do this insteadCheck the name, the parameters, and the return type, one at a time.
Common mistake Assuming one interface can describe two subroutines
Interface File Versus Class File
A functional interface holds the specification of a subroutine, while a class that provides the subroutine holds the working code for it. Opening the interface file, you should expect to find the name, the parameters, and the return type, and you should not expect to find a body that does the work. The table compares the two files for the valueAt subroutine.
| Part of the subroutine | Functional interface file | Class file that implements it |
|---|---|---|
| Name (valueAt) | Present | Present |
| Parameters (one double) | Present | Present |
| Return type (double) | Present | Present |
| Working code that computes the answer | Absent | Present |
What each file contains for the same subroutine
Checking Your Reading
Take the declaration interface PrintLines { void printLines(String[] lines, int count); }. A teammate writes a call that passes one String array and then expects a double back. Name every fact of the specification that the call disagrees with, and state what you would change in the call to make it match.
Hints
- Compare the call against the three facts: the name, the parameters, and the return type.
- Count the arguments the call passes and compare them with the parameters listed in the declaration.
- A void return type means no value comes back to the caller.
Key Takeaways
- A functional interface is a Java file that holds a specification for exactly one subroutine, and it is defined in a .java file just as a class is.
- The specification records three facts: the subroutine's name, its parameters (how many and their types), and its return type.
- The parameter name appears in the declaration but is not really part of the specification, so compare parameter counts and types when checking a use.
- A functional interface file holds the shape of the subroutine, not the computation; to find where a call diverges, compare it fact by fact against the declaration.