Interfaces
Problem
Salaries and invoices
At the end of the month the company pays two kinds of things: the employees' salaries and the suppliers' invoices. You want one array and one loop: who gets paid, how much, and the total at the end.
An Employee[] array will not work: an invoice is not an employee. Making it inherit from Employee is wrong. The sentence "an invoice is an employee" is not true, and the invoice would also get a name, a salary and a raise.
A common parent Payment will not work either. Manager already has a parent, Employee, and a class can have only one. Employees and invoices have nothing in common except that they can be paid.
The idea
An interface: a "can do" contract
An interface is a list of methods without bodies. It says what a class must be able to do, but not how:
interface Payable { int amountDue();
String payee();}class Invoice implements Payable { private String supplier; private int amount;
public Invoice(String supplier, int amount) { this.supplier = supplier; this.amount = amount; }
@Override public int amountDue() { return amount; }
@Override public String payee() { return supplier; }}
Invoice power = new Invoice("Power company", 340);System.out.println(power.payee() + ": €" + power.amountDue());Power company: €340You implement an interface with implements, not with extends. You can write @Override here too: the compiler checks that the method really comes from the interface.
Interface methods are always public. They are a promise to everyone, so in the class they have to be public too.
See it
One loop for everything that gets paid
The employee accepts the contract too. Its amount is the monthly salary:
class Employee implements Payable { // ... name, salary, the constructor, monthlyPay()
@Override public int amountDue() { return monthlyPay(); }
@Override public String payee() { return name; }}Now both the employees and the invoices are Payable. An interface is a type: it can be the type of a variable, a parameter or an array.
Payable[] payments = { new Employee("Ivan", 2000), new Manager("Maria", 3000, 500), new Invoice("Power company", 340), new Invoice("Landlord", 1800)};int total = 0;for (Payable p : payments) { System.out.println(p.payee() + ": €" + p.amountDue()); total += p.amountDue();}System.out.println("Total: €" + total);Ivan: €2000Maria: €3500Power company: €340Landlord: €1800Total: €7640The manager writes nothing new. From Employee it inherits both the contract and amountDue(). And amountDue() calls monthlyPay(), which the manager has overridden. That is why Maria gets €3500.
Payable[] payments = { new Employee("Ivan", 2000), new Manager("Maria", 3000, 500), new Invoice("Power company", 340)};int total = 0;for (Payable p : payments) { total += p.amountDue();}System.out.println("Total: €" + total);Step by step, in words
- The array is of type Payable. The arrows point to three classes, but they reach only their contract.
- total starts at 0.
- p points to the employee: amountDue() returns the salary, 2000.
- The manager is an employee, so the manager can be paid too. Salary plus bonus: 3500.
- The invoice is not an employee, but it keeps the same contract: 340.
- The loop does not know the classes. It only knows that each one can be paid.
Try it
What is the amount?
What will the program print? The classes are from the card above.
Payable p = new Manager("Maria", 3000, 500);System.out.println(p.amountDue());Manager is an Employee, and Employee implements the contract. So the manager implements it too, without writing it. The call goes to the manager's overridden method: 3000 + 500.
The idea
One parent, many contracts
Accounting also wants the invoices in CSV format: one "supplier;amount" line each. That is a second contract. A class can implement as many interfaces as it wants, separated by commas:
interface Exportable { String toCsv();}
class Invoice implements Payable, Exportable { // ... as before
@Override public String toCsv() { return supplier + ";" + amount; }}Exportable[] forAccounting = { new Invoice("Power company", 340), new Invoice("Landlord", 1800) };for (Exportable e : forAccounting) { System.out.println(e.toCsv());}Power company;340Landlord;1800If the class has both a parent and interfaces, the parent comes first:
class Manager extends Employee implements Exportable {There is one parent and there are many contracts. That way a class whose parent is already taken can still go into a common array with completely different classes.
Try it
Find the bug
Pick the line with the bug.
public is missing. Interface methods are promised to everyone, and without a modifier the method is hidden. The compiler says: "attempting to assign weaker access privileges; was public".Try it
What is missing?
class Invoice Payable {
// ...
}implements. For a parent class it is extends, for an interface it is implements.The idea
Abstract class or interface?
Both are parents you cannot create objects from, and both put obligations on the classes below them. The difference is in what they say:
| Abstract class | Interface | |
|---|---|---|
| Says | "X is a Y" | "X can" |
| How many per class | one | as many as you want |
| Fields and constructor | yes | no |
| Shared code | yes | hardly any |
- An abstract class, when the classes below it are one kind of thing and share code and data: shapes have a name and a description, employees have a name and a salary.
- An interface, when the classes are different but need a common ability: an employee and an invoice "can be paid", a bird and a plane "can fly".
Employee is the parent of the manager and the intern, and at the same time it implements Payable.More precisely, if you are curious
- Since Java 8 an interface can have a default method (
default), with a body. Classes get it ready-made, and they can also override it. That way you can add a new method to an interface without breaking the classes that already implement it:
interface Flyable { void fly();
default void glide() { System.out.println("Gliding through the air"); }}
class Bird implements Flyable { @Override public void fly() { System.out.println("The bird flaps its wings"); }}
class Plane implements Flyable { @Override public void fly() { System.out.println("The plane takes off"); }}
Flyable[] flyers = { new Bird(), new Plane() };for (Flyable f : flyers) { f.fly(); f.glide();}The bird flaps its wingsGliding through the airThe plane takes offGliding through the air- An interface cannot have object fields. It can have constants: every field in it is
public static final.
Exercise
Contracts
Solve the tasks in your editor.
1. Horn. An interface Honkable with a method honk(). SimpleCar from the module "Classes and objects" implements it.
Beep beep!2. Details. An interface Printable with a method printDetails(). Implement it in Person, and have Student (a child of Person) override it and add the grade. Put both of them in a Printable[] array.
Name: Georgi, age: 45Name: Elena, age: 19Average grade: 5.53. Dance. An interface Danceable with a method dance(). DanceMove (a name and a duration) implements it.
Dancing salsa for 30 seconds4. DJ. A class Song with a title. An interface Playable with a method play(). DJ implements it and has a method playSong(Song song) that remembers the song and plays it. If there is no song yet, play() says so.
No song selectedPlaying 'Bohemian Rhapsody'5. The pond. Interfaces Swimmable with swim() and Flyable with fly(). Fish swims. Duck both swims and flies.
The fish swims.The duck swims.The duck flies.More tasks
- Discount. An interface Discountable with a method that returns the price after the discount. Implement it in
Product(10% off) and inSubscription(25% off for students). Work out the total of a cart with a €50 product and a €12 student subscription: €45 + €9 = €54. - CSV for everyone. Make
Employeeimplement Exportable too, with a "name;monthly salary" line. Put employees and invoices in one Exportable[] array.
Test