Interfaces
Every wall socket in the country has the same shape. A phone charger, a lamp, and a rice cooker all plug in, because they all agreed to the same plug.
In Polymorphism, one loop worked with rectangles, circles, and squares, because they all shared a parent class, Shape. But inheritance has limits. A class can only have one parent. And sometimes, classes that have nothing in common as families still need to share one ability.
Think about "things that can be paid for" in a school. A tuition bill, a cafeteria order, and a field trip fee. They aren't the same kind of thing at all. None of them "is a" version of another. But every one of them has an amount to pay. Java's tool for describing a shared ability like that is the interface.
Plugs and sockets
A wall socket doesn't care what you plug into it. A lamp, a phone charger, and a rice cooker have nothing else in common: different makers, different jobs, different insides. But they all have a plug of the agreed shape, so they all work with any socket.
An interface is that agreed plug shape. It lists what a class must be able to do, without saying how. Any class that implements the interface promises to provide those abilities. And any code that needs those abilities can work with every one of those classes.
Declaring an interface
interface Payable {
double getAmountDue();
String getDescription();
}It looks like a class, but with the word interface, and the methods have no bodies, just a signature and a semicolon. Each one is a promise: "anything Payable will have these methods." Interface methods are automatically public, so you don't need to write it here.
Interface names are often adjectives ending in -able or -ible, describing an ability: Payable, Printable, Comparable. That's a convention, not a rule. Nouns like Shape or List are common too.
Implementing an interface
A class signs the contract with the word implements, then provides every method the interface lists:
class TuitionBill implements Payable {
private String studentName;
private double amount;
TuitionBill(String studentName, double amount) {
this.studentName = studentName;
this.amount = amount;
}
@Override
public double getAmountDue() {
return amount;
}
@Override
public String getDescription() {
return "Tuition for " + studentName;
}
}The methods must be public, to match the interface. And @Override works here too, with the same benefit you saw in Inheritance: the compiler checks that the method really matches the contract.
If the class forgets one of the methods, it doesn't compile:
class CafeteriaOrder implements Payable {
@Override
public double getAmountDue() {
return 85.0;
}
}
// error: CafeteriaOrder is not abstract and does not override abstract method getDescription() in PayableA promise is a promise. The compiler holds every class to the whole contract.
Using the interface as a type
Here's the payoff. An interface is a type, just like a class, so you can have variables, parameters, and lists of that type:
ArrayList<Payable> bills = new ArrayList<>();
bills.add(new TuitionBill("Maria", 15000));
bills.add(new CafeteriaOrder("Adobo meal", 2));
bills.add(new TripFee("Museum trip", 350, false));
double total = 0;
for (Payable bill : bills) {
System.out.println(bill.getDescription() + ": " + bill.getAmountDue());
total += bill.getAmountDue();
}This is polymorphism again, but without a shared parent class. TuitionBill, CafeteriaOrder, and TripFee are completely unrelated classes. The only thing they share is the promise, and that's all the loop needs.
You can't create an object of an interface itself, though. new Payable() is an error, the same way you can't buy "a plug shape" at a store. You buy a lamp that has one.
Several interfaces at once
A class can extend only one parent class, but it can implement as many interfaces as it wants, separated by commas:
interface Printable {
void print();
}
class TripFee implements Payable, Printable {
...
}A lamp can have a standard plug and a standard bulb socket and a standard switch. Each interface is one more ability the class promises.
And a class can do both, extending a parent and implementing interfaces:
class Student extends Person implements Printable {
...
}Default methods
Sometimes every class that implements an interface would write the exact same method. Since Java 8, an interface can provide a ready-made version with the word default:
interface Payable {
double getAmountDue();
String getDescription();
default String getReceiptLine() {
return String.format("%-25s %10.2f", getDescription(), getAmountDue());
}
}Every Payable class gets getReceiptLine() for free, built from the two methods it was required to write. A class can still override it if it needs something different.
Interfaces you'll meet in Java itself
Java's own library uses interfaces everywhere. A few you'll run into:
Listis an interface, andArrayListimplements it. You'll often seeList<String> names = new ArrayList<>();, which says "I need a list, and I happen to be using an ArrayList." The rest of the code only relies on theListpromises.Comparablelets objects say how they should be sorted, which is whatCollections.sortuses to put them in order.Runnabledescribes "a task that can be run," which shows up when programs do several things at once.
Interface or abstract class?
Both define something other classes must fill in, so how do you choose?
- Use an abstract class when the classes are truly one family ("is a") and share real code and fields. A
Circleis aShape. - Use an interface when you're describing an ability that unrelated classes can have. A tuition bill and a cafeteria order both can be paid, but neither is a kind of the other.
When you're unsure, lean toward an interface. A class can have many of them, so you're never locked in.
Try it
This program has three unrelated classes that all implement Payable, one of which also implements Printable. One loop prints receipt lines using the interface's default method and adds up the total. Predict the output.
Running Java here
Running Java right in the browser is coming to Syntaxia soon. On your own computer, save this program as Main.java and run it with java Main.java, or use your editor's Run button.
Check your prediction
Tuition for Maria 15000.00
Adobo meal x2 170.00
Museum trip (half price) 175.00
TOTAL 15345.00
Permission slip: Museum trip, please pay 175.0None of the three classes wrote getReceiptLine(); they all got it from the interface's default method, which calls each class's own getDescription() and getAmountDue(). Only trip can call print(), because only TripFee implements Printable.
Try it yourself
- Add a
LibraryFineclass that implementsPayable, charging 5 pesos per day late. Add one to the list. Did the loop need to change? - Remove the
getDescription()method fromCafeteriaOrder. Read the compiler's message carefully. What is it telling you? - Make
TuitionBillimplementPrintabletoo, and give it aprint()method. Then, after the loop, callprint()on every bill that isPrintable, usinginstanceof.
Check your understanding
Up next
You've used static on methods and constants since the very first chapter, and this in your constructors. It's time to understand both properly, in static and this.
