Java Interfaces
Updated 9 min read Aimerscrew
A Java interface describes what an object can do without saying how it does it. It's a list of method promises, a contract, that any class can sign up for, no matter which family the class belongs to. In this lesson, you'll learn how to write and implement interfaces, how one class can have several of them, what default and static methods are, and how to choose between an interface and an abstract class.
🌍 Real-life example
A USB-C port is an interface. Your phone, your laptop, an electric toothbrush and a pair of headphones are completely different products from different companies, but they all promise the same thing: "plug a USB-C cable in here and I'll charge". The charger doesn't care what the device is. It only cares that the device keeps that promise.
What is an interface in Java?
An interface is a reference type that lists method signatures a class must provide. Think of it as a capability: "can be charged", "can be printed", "can be shipped". You declare one with the interface keyword, and a class promises to fulfil it with the implements keyword:
public class ChargingStation {
public static void main(String[] args) {
Chargeable[] devices = { new Phone(), new ElectricCar(), new Laptop() };
for (Chargeable d : devices) {
d.charge(30);
}
}
}
interface Chargeable {
void charge(int minutes); // automatically public and abstract
}
class Phone implements Chargeable {
private int battery = 20;
@Override
public void charge(int minutes) {
battery = Math.min(100, battery + minutes * 2);
System.out.println("Phone battery: " + battery + "%");
}
}
class ElectricCar implements Chargeable {
private int rangeKm = 50;
@Override
public void charge(int minutes) {
rangeKm += minutes * 5;
System.out.println("Car range: " + rangeKm + " km");
}
}
class Laptop implements Chargeable {
private int battery = 55;
@Override
public void charge(int minutes) {
battery = Math.min(100, battery + minutes);
System.out.println("Laptop battery: " + battery + "%");
}
}
Phone battery: 80%
Car range: 200 km
Laptop battery: 85%
A phone, a car and a laptop have nothing else in common, so making them share a parent class would be strange. The interface lets them share just the one ability that matters here. The array type is the interface, and polymorphism runs each class's own charge().
The rules of implementing an interface
- Methods declared in an interface without a body are automatically
publicandabstract. You don't need to write either word. - The implementing class must provide every abstract method, or be declared
abstractitself. - The implementing method must be
public. Leaving outpublicgives the errorattempting to assign weaker access privileges; was public. - Use
@Overrideon implemented methods too, so the compiler checks the name and parameters. - You can't create an object of an interface with
new Chargeable(), but you can use it as a variable, parameter or array type.
Can a class implement multiple interfaces?
Yes. A class can extend only one class, but it can implement as many interfaces as you like, separated by commas. This is how Java gives you the useful part of multiple inheritance without the confusion.
public class DroneFleet {
public static void main(String[] args) {
DeliveryDrone drone = new DeliveryDrone("DX-7");
drone.charge(15);
System.out.println(drone.location());
Chargeable c = drone; // the same object, seen as Chargeable
Trackable t = drone; // ...or as Trackable
c.charge(5);
System.out.println(t.location());
}
}
interface Chargeable {
void charge(int minutes);
}
interface Trackable {
String location();
}
class DeliveryDrone implements Chargeable, Trackable {
private String id;
private int battery = 40;
DeliveryDrone(String id) {
this.id = id;
}
@Override
public void charge(int minutes) {
battery = Math.min(100, battery + minutes * 3);
System.out.println(id + " battery: " + battery + "%");
}
@Override
public String location() {
return id + " is above Warehouse 3";
}
}
DX-7 battery: 85%
DX-7 is above Warehouse 3
DX-7 battery: 100%
DX-7 is above Warehouse 3
A charging station only needs to see the drone as Chargeable; a map screen only needs it as Trackable. Each part of the program sees just the abilities it cares about. A class can also extend a class and implement interfaces: class DeliveryDrone extends Aircraft implements Chargeable, Trackable (the extends part comes first).
Default methods, static methods and constants
Since Java 8, an interface can contain more than abstract methods:
- A default method has a body and the keyword
default. Implementing classes get it for free and may override it. - A static method belongs to the interface itself. You call it as
InterfaceName.method(); it isn't inherited by objects. - A field in an interface is automatically
public static final, which makes it a constant.
public class ShippingDesk {
public static void main(String[] args) {
Shippable[] parcels = { new Book("Java for Beginners", 0.4), new Laptop(2.1) };
for (Shippable p : parcels) {
printLabel(p);
}
System.out.println("Flat fee: " + Shippable.format(Shippable.BASE_FEE));
}
// Works with ANY Shippable, now and in the future
static void printLabel(Shippable item) {
System.out.println(item.label() + " -> " + Shippable.format(item.shippingCost()));
}
}
interface Shippable {
double BASE_FEE = 4.99; // a constant: public static final
double weightKg(); // abstract: each class must write it
String label();
default double shippingCost() { // default: a ready-made body
return BASE_FEE + weightKg() * 2.0;
}
static String format(double amount) { // static: belongs to the interface
return String.format("$%.2f", amount);
}
}
class Book implements Shippable {
private String title;
private double weight;
Book(String title, double weight) {
this.title = title;
this.weight = weight;
}
@Override
public double weightKg() {
return weight;
}
@Override
public String label() {
return "Book: " + title;
}
}
class Laptop implements Shippable {
private double weight;
Laptop(double weight) {
this.weight = weight;
}
@Override
public double weightKg() {
return weight;
}
@Override
public String label() {
return "Laptop";
}
@Override
public double shippingCost() { // overriding a default method
return Shippable.super.shippingCost() + 10.0; // fragile item fee
}
}
Book: Java for Beginners -> $5.79
Laptop -> $19.19
Flat fee: $4.99
Book uses the default shippingCost() as is. Laptop overrides it and calls the default version with the special syntax Shippable.super.shippingCost(), then adds a fragile-item fee. String.format() works like printf but returns the text instead of printing it.
📝 Note
Default methods were added so that Java could add new methods to old interfaces without breaking every class that already implemented them. Use them for sensible shared behavior, not as a way to stuff lots of code into an interface.
Programming to an interface
Look again at printLabel(Shippable item) above. Its parameter type is the interface, not Book or Laptop. That method will work with any class that implements Shippable, including classes nobody has written yet. This habit is called programming to an interface: declare variables and parameters using the most general type that has the methods you need.
You'll see this everywhere in real Java code. For example, programmers write List<String> names = new ArrayList<>(); rather than ArrayList<String> names, because List is an interface. You'll meet it in the Java Collections Framework.
💡 Tip
Name interfaces after an ability or a role. Adjectives ending in "-able" (Chargeable, Printable, Comparable) read nicely: "a Laptop is Shippable".
Interface vs abstract class: which should you use?
| Interface | Abstract class | |
|---|---|---|
| Keyword | interface / implements | abstract class / extends |
| How many per class? | Many | Only one |
| Instance fields (state) | No, only constants | Yes |
| Constructors | No | Yes |
| Methods with a body | default and static methods | Any normal method |
| Method access | Public by default | Any modifier |
| Relationship | "can do" (a capability) | "is a" (a family) |
| Best for | Unrelated classes sharing an ability | Related classes sharing code and data |
When in doubt, start with an interface. It keeps your classes free to extend something else later. You can also combine both: an abstract class that implements an interface and provides shared code for its subclasses.
Sealed interfaces (Java 17)
Normally, any class can implement an interface. Sometimes you want a closed list: "a parking lot vehicle is a Car or a Motorbike, nothing else". A sealed interface uses the sealed and permits keywords to name exactly which classes may implement it:
public class ParkingFees {
public static void main(String[] args) {
ParkedVehicle[] lot = { new Car(), new Motorbike(), new Car() };
double total = 0;
for (ParkedVehicle v : lot) {
total += v.hourlyRate() * 3;
}
System.out.printf("3 hours for %d vehicles: $%.2f%n", lot.length, total);
}
}
sealed interface ParkedVehicle permits Car, Motorbike {
double hourlyRate();
}
final class Car implements ParkedVehicle {
@Override
public double hourlyRate() {
return 2.50;
}
}
final class Motorbike implements ParkedVehicle {
@Override
public double hourlyRate() {
return 1.00;
}
}
3 hours for 3 vehicles: $18.00
Each permitted class must say how the chain continues: final (no more subclasses), sealed (its own closed list) or non-sealed (open again). If a Truck tries to implement ParkedVehicle, the compiler refuses: class is not allowed to extend sealed class: ParkedVehicle. Classes can be sealed in the same way.
Common mistakes with interfaces
1. Forgetting public on the implementing method
Interface methods are public, so void charge(int m) { } in the class fails with attempting to assign weaker access privileges; was public. Write public void charge(int m).
2. Using extends instead of implements
A class implements an interface: class Phone implements Chargeable. (An interface can extend another interface, though: interface FastChargeable extends Chargeable.)
3. Trying to store state in an interface
A field like int battery = 50; inside an interface becomes a shared public static final constant, not a per-object field. You can't change it. Keep object data in the classes.
4. Calling a static interface method on an object
Static interface methods are called through the interface name, as in Shippable.format(9.5). Writing item.format(9.5) fails with illegal static interface method call.
Practice exercise
🏋️ Try it yourself
Create a Printable interface with an abstract method print() and a default method printCopies(int copies) that prints [1/2] and so on before each copy. Implement it in an Invoice class (number and total) and a BoardingPass class (passenger, flight and seat). Put one of each in a Printable[] array and print two copies of everything.
Show solution
public class PrintShop {
public static void main(String[] args) {
Printable[] queue = {
new Invoice("INV-1042", 89.90),
new BoardingPass("Ana Costa", "LH 401", "14C")
};
for (Printable p : queue) {
p.printCopies(2);
}
}
}
interface Printable {
void print();
default void printCopies(int copies) {
for (int i = 1; i <= copies; i++) {
System.out.print("[" + i + "/" + copies + "] ");
print();
}
}
}
class Invoice implements Printable {
private String number;
private double total;
Invoice(String number, double total) {
this.number = number;
this.total = total;
}
@Override
public void print() {
System.out.printf("Invoice %s, total $%.2f%n", number, total);
}
}
class BoardingPass implements Printable {
private String passenger;
private String flight;
private String seat;
BoardingPass(String passenger, String flight, String seat) {
this.passenger = passenger;
this.flight = flight;
this.seat = seat;
}
@Override
public void print() {
System.out.println("Boarding pass: " + passenger + ", flight " + flight + ", seat " + seat);
}
}
[1/2] Invoice INV-1042, total $89.90
[2/2] Invoice INV-1042, total $89.90
[1/2] Boarding pass: Ana Costa, flight LH 401, seat 14C
[2/2] Boarding pass: Ana Costa, flight LH 401, seat 14C
What's next?
Every class you've written, whether it extends something or not, secretly has one more parent: Object. It gives every object methods like toString() and equals(). Next, learn how to use and override them in the Java Object class.
Key takeaways
- A Java interface is a contract: a list of methods that implementing classes promise to provide.
- A class uses implements to sign the contract and must provide every abstract method as a public method.
- A class can extend only one class but can implement many interfaces, separated by commas.
- Interfaces can contain default methods (with a body), static methods (called on the interface) and constants (public static final).
- Programming to an interface means using the interface as your variable and parameter type so any implementation fits.
- Use an interface for a shared ability across unrelated classes and an abstract class for related classes sharing code and state.
- A sealed interface (Java 17) lists exactly which classes may implement it with permits.
Frequently asked questions
What is an interface in Java?
An interface is a type that lists methods a class must provide, without saying how they work. A class implements the interface with the implements keyword and writes the method bodies itself.
Can a Java class implement more than one interface?
Yes. A class can implement any number of interfaces, for example class Drone implements Chargeable, Trackable. It can only extend one class, though.
What is a default method in a Java interface?
A default method is an interface method with a body, marked with the default keyword. Implementing classes inherit it automatically and can override it if they need different behavior.
Can an interface have variables in Java?
Only constants. Any field declared in an interface is automatically public, static and final, so it must be given a value and can never change.
What is the difference between an interface and an abstract class?
An interface describes a capability, has no instance fields or constructors, and a class can implement many. An abstract class describes a family, can have fields, constructors and any methods, and a class can extend only one.
Can you create an object of an interface?
No, new Chargeable() does not compile. You create objects of classes that implement the interface and can store them in variables of the interface type.