The labs on this site need JavaScript to run. The same content is written out below.
Fundamentals › Java
Interfaces
What does an interface look like in code, and what does it decide?
An interface is a contract that you declare with interface. It says, for example, "anything of type Payable must have a pay method", and it writes only the name of the method, with no body. A class declares with implements Payable that it will follow the contract, and it actually writes the pay method that the contract names. Then you can put an object of that class in a variable of type Payable. In this lab you remove the interface declaration, implements, and the pay method one at a time, and the same code is compiled and run each time. You see what the compiler reports and what is printed.
Checked against real execution · Twelve combinations were compiled with javax.tools and run on JDK 21, and the results were checked against the model
Concepts in this lab
- Interface
- This is a type that defines the list of methods (the contract) that a class must implement. It cannot have instance variables, and one class can implement several of them. If the calling code depends only on this type, it is easy to change the implementing class or to replace it with a fake implementation for testing.
- implements
- implements is the keyword in a class declaration that says which interface the class agrees to follow the contract of. You write it after the class name, as in class Card implements Payable, and you can write several interfaces separated by commas. A class that is not abstract and uses implements must write every abstract method of that interface itself, or inherit it from a superclass, and it is a compile-time error if even one is missing. When you use implements, the interface becomes a supertype of the class, so you can put an object of the class in a variable of that interface type. If a class has methods with the same names but does not use implements, it does not become that interface type.
- Abstract method
- An abstract method is a method that declares only the name, the parameters, and the return type, and has no implementation (no body). In an interface, if you write only a semicolon where the body would go, as in void pay();, it is an abstract method and is public even without writing abstract. If the body is a block of braces and not a semicolon, the method is an implementation even if the block is empty ({ }). A class that implements the interface that has an abstract method must write it, and it is a compile-time error if a class that is not abstract does not write it.
- Type
- A type is a name that decides what kind of value a variable can hold. The types of Java are divided into primitive types and reference types, and the reference types are class types, interface types, type variables, and array types. When you write a class Card or interface Payable declaration, you can use Card and Payable as types. On every line that puts a value in a variable, the compiler checks that the type of the value is the same as the type of the variable or a subtype of it. An interface that a class names with implements is a supertype of that class. If you use a name that is not declared as a type, the compiler cannot find that type and reports an error.
Try these
- Default settings — All three are there. Compilation succeeds and "paid by card" is printed.
- Remove implements — Even though the pay method is there, Card is not of type Payable, so there is a compile-time error.
- Remove the pay method — The method that the contract names was not written, so there is a compile-time error.
- Leave the body of the pay method empty — Even with an empty body, the method is written. Compilation succeeds and nothing is printed.
- Remove the interface declaration — There is no type named Payable, so there is a compile-time error.
Running with the default settings
- The compiler reads the source from the top. First it reads the interface Payable declaration, and from this declaration it learns the type named Payable. The Payable type is created by this declaration. The line void pay(); has only a name and no body, and in an interface this shape is a contract: "anything of type Payable must have a pay method" (an abstract method).
- The compiler reads the line class Card implements Payable. It is a declaration that Card will follow the contract of Payable. So the pay method that the contract names must be written inside Card.
- The compiler looks inside Card for the pay method that the contract names. There is a line public void pay() { ... } and it has a body, so the contract is followed.
- The compiler checks the line Payable p = new Card();. Card agreed to the contract with implements Payable, so it is fine to put a Card object in the variable p of type Payable. This is not running. It is the compiler checking in advance whether it is allowed.
- Compilation succeeds and the Main class file that will run is created. Whether the contract is followed was not checked at run time. The compiler checked it just now, while compiling.
- You run it with java Main. main starts, and new Card() creates a Card object and puts it in the variable p of type Payable.
- p.pay() runs. The object held in p is a Card object, so the pay method written in Card runs. The println in the body prints "paid by card".
Result of this run
- An interface is a contract that you declare with interface: "anything of this type must have this method". It writes only the name of the method and no body. The interface Payable declaration creates the type named Payable, and Card declares with implements Payable that it will follow the contract and then actually writes the pay method.
- So you could put a Card object in the variable p of type Payable, and p.pay() ran the pay of Card. Whether the contract was followed was not checked at run time. The compiler checked it while compiling.
Code
// Contract: anything of type Payable must have a pay method (only the name is written)
interface Payable {
void pay();
}
// Declaration that Card will follow the contract
class Card implements Payable {
// Actually writes the method that the contract names
public void pay() {
System.out.println("paid by card");
}
}
public class Main {
public static void main(String[] args) {
// Put a Card object in a variable of type Payable
Payable p = new Card();
p.pay();
}
}
Assumptions of this model
The compiler and runtime are JDK 21.0.12.1 (javac 21). For all twelve combinations, the same source as the code on the screen (identical line by line) was compiled with javax.tools. The ones that compiled had their class files loaded by a new class loader and Main.main was run, and the results were checked against the standard output that was captured. What was checked is whether compilation succeeded, the diagnostic code of the first error (compiler.err.cant.resolve, cant.resolve.location, prob.found.req, and does.not.override.abstract, shown on the screen as the three error descriptions), the number of errors and the lines where they occurred, whether the Main class file was created, and the output when run. The documentation checked was the Java Language Specification (Java SE 21), opened directly. The rule that an interface method that ends with a semicolon and has no body is an abstract method and is public follows section 9.4. The rule that a class that is not abstract must implement all the abstract methods of the interfaces it implements follows sections 8.1.5 and 8.1.1.1. The rule that if the method body is a block and not a semicolon the method is an implementation, and that the block may be empty, follows section 8.4.7 and the example (alert() { }) in section 8.1.1.1. The rule that interfaces and classes are both reference types and that an interface a class implements is a supertype of the class follows sections 4.3 and 4.10.2. The rule that a value of a subtype can be put in a variable of its supertype follows sections 5.1.5 and 5.2. The kind of error, the number of errors, and the line where each occurs are not set by the specification. They were observed with this javac. When there are several errors, javac reports all of them, but the table shows only the first. Even when compilation failed, some class files such as Card were created, but the Main class file was never created, so the table says it cannot run. An interface can also have default methods, static methods, and constants, and one class can implement several interfaces, but this lab did not cover them. How interfaces are used and how they differ from abstract classes are covered in "In-depth lab: Interfaces and abstract classes".
Previous lab: Inheritance · Next lab: Exceptions