Java course menu
Java Interfaces: implements, Default Methods and Functional Interfaces
Learn Java interfaces: declaring and implementing interfaces, programming to an interface, implementing several interfaces, default and static methods, constants, functional interfaces for lambdas, sealed interfaces, and interface vs abstract class.
On this page
An interface describes a set of abilities that a class promises to provide, without saying how. Any class can implement an interface, and a class can implement many interfaces. Interfaces are everywhere in Java: List, Map, Comparable and Runnable are all interfaces.
Declaring and implementing an interface#
import java.util.List;
interface Payment {
boolean pay(double amount);
String method();
}
class CardPayment implements Payment {
@Override
public boolean pay(double amount) {
return amount <= 50_000;
}
@Override
public String method() {
return "card";
}
}
class UpiPayment implements Payment {
@Override
public boolean pay(double amount) {
return amount <= 100_000;
}
@Override
public String method() {
return "UPI";
}
}
public class Main {
public static void main(String[] args) {
List<Payment> options = List.of(new CardPayment(), new UpiPayment());
for (Payment p : options) {
System.out.println(p.method() + " can pay 75000: " + p.pay(75_000));
}
}
}card can pay 75000: false
UPI can pay 75000: true- Interface methods are
publicandabstractby default, so implementing methods must bepublic. implements Paymentis a promise: the compiler checks that the class provides every method.
Program to an interface#
Declaring variables, parameters and return types with the interface type, rather than a specific class, keeps code flexible:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Main {
static int countLong(List<String> words) { // accepts ANY List implementation
int count = 0;
for (String w : words) {
if (w.length() > 4) count++;
}
return count;
}
public static void main(String[] args) {
List<String> a = new ArrayList<>(List.of("apple", "fig", "banana"));
List<String> b = new LinkedList<>(List.of("kiwi", "cherry"));
System.out.println(countLong(a) + " " + countLong(b));
}
}2 1That is why Java code usually writes List<String> list = new ArrayList<>();: the rest of the code depends only on List, so the implementation can change in one place.
Implementing several interfaces#
A class extends one class, but can implement any number of interfaces:
interface Printable {
String toText();
}
interface Saveable {
boolean save();
}
class Invoice implements Printable, Saveable {
private final int number;
Invoice(int number) { this.number = number; }
@Override
public String toText() { return "Invoice #" + number; }
@Override
public boolean save() {
System.out.println("saving " + toText());
return true;
}
}
public class Main {
static void print(Printable item) {
System.out.println(item.toText());
}
public static void main(String[] args) {
Invoice inv = new Invoice(1042);
print(inv);
inv.save();
}
}Invoice #1042
saving Invoice #1042print accepts anything printable and knows nothing about invoices.
Default and static methods#
Since Java 8, interfaces can contain:
- default methods: methods with a body that implementing classes inherit (and may override). They let an interface grow without breaking existing implementations.
- static methods: helpers called on the interface name.
interface Greeter {
String name();
default String greet() { // inherited by every implementation
return "Hello, " + name() + "!";
}
static Greeter of(String name) { // a factory method
return () -> name;
}
}
class Robot implements Greeter {
@Override public String name() { return "R2"; }
@Override public String greet() { return "BEEP. " + name() + " ONLINE."; }
}
public class Main {
public static void main(String[] args) {
System.out.println(Greeter.of("Asha").greet());
System.out.println(new Robot().greet());
}
}Hello, Asha!
BEEP. R2 ONLINE.() -> name is a lambda: because Greeter has exactly one abstract method, a lambda can implement it. That leads to the next section.
Functional interfaces#
An interface with exactly one abstract method is a functional interface, and can be implemented with a short lambda instead of a whole class. The annotation @FunctionalInterface asks the compiler to check this:
@FunctionalInterface
interface Operation {
double apply(double a, double b);
}
public class Main {
static double calculate(double a, double b, Operation op) {
return op.apply(a, b);
}
public static void main(String[] args) {
Operation add = (a, b) -> a + b;
Operation power = Math::pow; // a method reference
System.out.println(calculate(8, 2, add));
System.out.println(calculate(8, 2, (a, b) -> a * b));
System.out.println(calculate(2, 10, power));
}
}10.0
16.0
1024.0The standard library provides ready-made functional interfaces in java.util.function, such as Predicate, Function and Consumer. They are covered in Lambdas and Functional Interfaces.
Constants in interfaces#
Fields declared in an interface are automatically public static final constants. They work, but a dedicated class or an enum is usually a clearer home for constants.
Sealed interfaces (Java 17)#
A sealed interface lists exactly which classes may implement it. The compiler then knows every possible type, which pairs perfectly with switch pattern matching:
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
public class Main {
static double area(Shape s) {
return switch (s) { // no default needed: the compiler knows every case
case Circle c -> Math.PI * c.r() * c.r();
case Square q -> q.side() * q.side();
};
}
public static void main(String[] args) {
System.out.printf("%.2f %.2f%n", area(new Circle(1)), area(new Square(3)));
}
}3.14 9.00You will learn about record in the next lesson.
Interface or abstract class?#
Use an interface to describe a capability (Comparable, Payment, Printable) that unrelated classes can share, and as the type in your variables and parameters. Use an abstract class when closely related classes share state and code. When in doubt, start with an interface.
Common mistakes#
Practice#
Practice exercise
Define an interface Shape with double area() and a default method String describe() that returns "Area: <area>" with two decimals. Implement it with Rectangle and Circle, then print the description of a 4 × 2.5 rectangle and a circle of radius 1.
Show solution
interface Shape {
double area();
default String describe() {
return String.format("Area: %.2f", area());
}
}
class Rectangle implements Shape {
private final double w;
private final double h;
Rectangle(double w, double h) { this.w = w; this.h = h; }
@Override public double area() { return w * h; }
}
class Circle implements Shape {
private final double r;
Circle(double r) { this.r = r; }
@Override public double area() { return Math.PI * r * r; }
}
public class ShapesDemo {
public static void main(String[] args) {
Shape[] shapes = {new Rectangle(4, 2.5), new Circle(1)};
for (Shape s : shapes) {
System.out.println(s.describe());
}
}
}Area: 10.00
Area: 3.14Key takeaway#
Finished this lesson?