Java Interfaces
Java Interfaces
An interface in Java is a contract — a set of method signatures that any implementing class must provide. When a class implements an interface, it makes a promise: "I will implement every method this interface defines." The calling code holds a reference to the interface type, not the concrete class, so any class that fulfils the contract can be substituted without changing the caller.
Interfaces are Java's mechanism for defining what an object can do without dictating how. They are also the solution to Java's single-class-inheritance limitation — a class can implement any number of interfaces, gaining multiple type identities simultaneously.
What Is an Interface in Java?
An interface is a reference type — like a class — that contains only abstract method signatures, constants, and since Java 8, default and static methods. A class uses the implements keyword to declare that it fulfils an interface's contract. Once it does, objects of that class can be referenced through the interface type.
Key characteristics:
- ›All methods are
publicby default - ›Abstract methods have no body — implementing classes must provide one
- ›Fields are implicitly
public static final— they are constants - ›Interfaces cannot be instantiated
- ›A class can implement multiple interfaces simultaneously
- ›An interface can extend multiple other interfaces
Interface Syntax — All Forms
Basic Interface
// File: Printable.java
public interface Printable {
// Abstract method — no body, implementing class must provide one
void print();
// Constant — implicitly public static final
int MAX_PRINT_WIDTH = 120;
}Interface with Default and Static Methods (Java 8+)
// File: Auditable.java
public interface Auditable {
// Abstract — must be implemented
String getEntityId();
String getEntityType();
// Default method — has a body, implementing class inherits it automatically
default String buildAuditLog(String action) {
return "[AUDIT] " + getEntityType() + " | ID: " + getEntityId()
+ " | Action: " + action
+ " | At: " + java.time.LocalDateTime.now();
}
// Static method — belongs to the interface, called on interface name
static String formatTimestamp(java.time.LocalDateTime dt) {
return dt.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}Interface Extending Multiple Interfaces
// File: TrackableEntity.java
public interface TrackableEntity extends Printable, Auditable {
// Adds methods to the combined contract
String getStatus();
default String getFullSummary() {
return getEntityType() + " [" + getEntityId() + "] — " + getStatus();
}
}Implementing an Interface
// File: Order.java
public class Order implements TrackableEntity {
private final String orderId;
private final String customerName;
private String status;
public Order(String orderId, String customerName) {
this.orderId = orderId;
this.customerName = customerName;
this.status = "PLACED";
}
// Required by Auditable
@Override
public String getEntityId() { return orderId; }
@Override
public String getEntityType() { return "Order"; }
// Required by TrackableEntity
@Override
public String getStatus() { return status; }
// Required by Printable
@Override
public void print() {
System.out.println("Order: " + orderId
+ " | Customer: " + customerName
+ " | Status: " + status);
}
public void updateStatus(String newStatus) {
System.out.println(buildAuditLog("STATUS_UPDATE → " + newStatus));
this.status = newStatus;
}
}// File: InterfaceBasicDemo.java
public class InterfaceBasicDemo {
public static void main(String[] args) {
Order order = new Order("ORD-4521", "Priya Mehta");
// Polymorphic references — same object through different interface lenses
Printable printable = order;
Auditable auditable = order;
TrackableEntity trackable = order;
order.print(); // Printable
System.out.println(auditable.buildAuditLog("VIEWED")); // default method
System.out.println(trackable.getFullSummary()); // default method
order.updateStatus("SHIPPED");
order.print();
}
}Output:
Order: ORD-4521 | Customer: Priya Mehta | Status: PLACED
[AUDIT] Order | ID: ORD-4521 | Action: VIEWED | At: 2024-01-15T10:30:00
Order [ORD-4521] — PLACED
[AUDIT] Order | ID: ORD-4521 | Action: STATUS_UPDATE → SHIPPED | At: 2024-01-15T10:30:01
Order: ORD-4521 | Customer: Priya Mehta | Status: SHIPPED
The Order object can be referenced as Printable, Auditable, or TrackableEntity — each view exposes a different subset of the object's capabilities. This is multiple type inheritance through interfaces: one object, multiple contracts fulfilled simultaneously.
Default Methods — Java 8+
A default method has a body in the interface itself. Every implementing class automatically inherits it without writing anything. Implementing classes can override it if needed.
Default methods were introduced in Java 8 to allow interfaces to evolve — adding new methods without breaking every existing implementing class. Before Java 8, adding any method to an interface forced every implementor to add the method or fail to compile.
// File: DeliveryAgent.java
public interface DeliveryAgent {
String getAgentId();
String getAgentName();
double getCurrentLatitude();
double getCurrentLongitude();
// Default — available to all implementations without writing it
default String getLocationSummary() {
return getAgentName() + " | Location: ("
+ getCurrentLatitude() + ", "
+ getCurrentLongitude() + ")";
}
default boolean isWithinRadius(double targetLat, double targetLng,
double radiusKm) {
double latDiff = Math.abs(getCurrentLatitude() - targetLat);
double lngDiff = Math.abs(getCurrentLongitude() - targetLng);
double approxDistKm = Math.sqrt(latDiff * latDiff + lngDiff * lngDiff) * 111;
return approxDistKm <= radiusKm;
}
// Static — utility method on the interface itself
static String formatCoordinates(double lat, double lng) {
return String.format("(%.4f, %.4f)", lat, lng);
}
}// File: BikeAgent.java
public class BikeAgent implements DeliveryAgent {
private final String agentId;
private final String agentName;
private double latitude;
private double longitude;
private String vehicleNumber;
public BikeAgent(String agentId, String agentName,
double latitude, double longitude, String vehicleNumber) {
this.agentId = agentId;
this.agentName = agentName;
this.latitude = latitude;
this.longitude = longitude;
this.vehicleNumber = vehicleNumber;
}
@Override public String getAgentId() { return agentId; }
@Override public String getAgentName() { return agentName; }
@Override public double getCurrentLatitude() { return latitude; }
@Override public double getCurrentLongitude() { return longitude; }
public void updateLocation(double lat, double lng) {
this.latitude = lat;
this.longitude = lng;
}
// Optional — overrides the default only if customisation is needed
@Override
public String getLocationSummary() {
return DeliveryAgent.super.getLocationSummary() // calls interface default
+ " | Vehicle: " + vehicleNumber;
}
}// File: DefaultMethodDemo.java
public class DefaultMethodDemo {
public static void main(String[] args) {
BikeAgent agent = new BikeAgent(
"AGT-001", "Rahul Kumar",
12.9716, 77.5946, "KA05AB1234" // Bengaluru coordinates
);
System.out.println(agent.getLocationSummary()); // overridden default
// Interface static method — called on interface name
System.out.println(DeliveryAgent.formatCoordinates(
agent.getCurrentLatitude(), agent.getCurrentLongitude()));
// Check if agent is within 5 km of delivery point
double deliveryLat = 12.9352;
double deliveryLng = 77.6245;
System.out.println("Within 5 km of delivery: "
+ agent.isWithinRadius(deliveryLat, deliveryLng, 5.0));
}
}Output:
Rahul Kumar | Location: (12.9716, 77.5946) | Vehicle: KA05AB1234
(12.9716, 77.5946)
Within 5 km of delivery: true
When BikeAgent calls DeliveryAgent.super.getLocationSummary(), it invokes the interface's default implementation and then appends vehicle-specific data. This is how a class extends a default method rather than completely replacing it.
Multiple Interface Implementation
A class can implement any number of interfaces — this is Java's answer to multiple inheritance. Each interface adds a new type identity and a new set of required methods.
// File: Identifiable.java
public interface Identifiable {
String getId();
}// File: Priceable.java
public interface Priceable {
double getPrice();
default double getPriceWithGst() { return getPrice() * 1.18; }
}// File: Searchable.java
public interface Searchable {
String getSearchKeywords();
default boolean matchesQuery(String query) {
return getSearchKeywords().toLowerCase().contains(query.toLowerCase());
}
}// File: Product.java
// One class — three interface contracts fulfilled simultaneously
public class Product implements Identifiable, Priceable, Searchable {
private final String productId;
private final String productName;
private final String category;
private final double price;
public Product(String productId, String productName,
String category, double price) {
this.productId = productId;
this.productName = productName;
this.category = category;
this.price = price;
}
@Override public String getId() { return productId; }
@Override public double getPrice() { return price; }
@Override
public String getSearchKeywords() {
return productName + " " + category + " " + productId;
}
public String getName() { return productName; }
public String getCategory() { return category; }
public String getSummary() {
return productId + " | " + productName + " | Rs." + price;
}
}// File: MultipleInterfaceDemo.java
import java.util.List;
public class MultipleInterfaceDemo {
// Each method accepts a specific interface — Product satisfies all three
public static void displayId(Identifiable item) {
System.out.println("ID: " + item.getId());
}
public static void displayPrice(Priceable item) {
System.out.printf("Price: Rs.%.2f | With GST: Rs.%.2f%n",
item.getPrice(), item.getPriceWithGst());
}
public static void searchProducts(List<Searchable> items, String query) {
System.out.println("Searching for: \"" + query + "\"");
items.stream()
.filter(item -> item.matchesQuery(query))
.forEach(item -> System.out.println(" Found: " + item.getSearchKeywords()));
}
public static void main(String[] args) {
Product laptop = new Product("SKU-001", "Laptop Pro 15", "Electronics", 85000.0);
Product mouse = new Product("SKU-002", "Wireless Mouse", "Electronics", 1299.0);
Product notebook = new Product("SKU-003", "A4 Notebook 200pg","Stationery", 89.0);
// Same object passed to methods expecting different interface types
displayId(laptop);
displayPrice(laptop);
System.out.println();
displayId(mouse);
displayPrice(mouse);
System.out.println();
searchProducts(List.of(laptop, mouse, notebook), "electronics");
System.out.println();
searchProducts(List.of(laptop, mouse, notebook), "laptop");
}
}Output:
ID: SKU-001
Price: Rs.85000.00 | With GST: Rs.100300.00
ID: SKU-002
Price: Rs.1299.00 | With GST: Rs.1532.82
Searching for: "electronics"
Found: Laptop Pro 15 Electronics SKU-001
Found: Wireless Mouse Electronics SKU-002
Searching for: "laptop"
Found: Laptop Pro 15 Electronics SKU-001
Product is simultaneously Identifiable, Priceable, and Searchable. A method that needs only a price — displayPrice(Priceable) — does not need to know the full Product type. This is the Interface Segregation Principle in practice: callers depend only on the capability they need.
Functional Interfaces
A functional interface is an interface with exactly one abstract method. It can be implemented using a lambda expression or method reference — the lambda expression is the implementation of the single abstract method.
Java 8+ provides the @FunctionalInterface annotation — it tells the compiler to enforce the one-abstract-method rule and report an error if violated.
// File: OrderFilter.java
@FunctionalInterface
public interface OrderFilter {
// Exactly one abstract method — what makes this a functional interface
boolean accepts(String orderId, double amount, String status);
// Default methods are allowed — they don't count as abstract
default OrderFilter and(OrderFilter other) {
return (id, amt, st) -> this.accepts(id, amt, st) && other.accepts(id, amt, st);
}
}// File: FunctionalInterfaceDemo.java
import java.util.List;
public class FunctionalInterfaceDemo {
record OrderRecord(String orderId, double amount, String status) {}
public static List<OrderRecord> filterOrders(List<OrderRecord> orders,
OrderFilter filter) {
return orders.stream()
.filter(o -> filter.accepts(o.orderId(), o.amount(), o.status()))
.toList();
}
public static void main(String[] args) {
List<OrderRecord> orders = List.of(
new OrderRecord("ORD-001", 1499.0, "PENDING"),
new OrderRecord("ORD-002", 299.0, "SHIPPED"),
new OrderRecord("ORD-003", 5999.0, "PENDING"),
new OrderRecord("ORD-004", 1800.0, "DELIVERED"),
new OrderRecord("ORD-005", 150.0, "PENDING")
);
// Lambda implements OrderFilter — no class needed
OrderFilter highValue = (id, amt, st) -> amt > 1000.0;
OrderFilter pendingOnly = (id, amt, st) -> "PENDING".equals(st);
// Compose two filters using the default and() method
OrderFilter highValuePending = highValue.and(pendingOnly);
System.out.println("High-value orders (> Rs.1000):");
filterOrders(orders, highValue)
.forEach(o -> System.out.println(" " + o.orderId() + " | Rs." + o.amount()));
System.out.println("\nPending orders only:");
filterOrders(orders, pendingOnly)
.forEach(o -> System.out.println(" " + o.orderId() + " | " + o.status()));
System.out.println("\nHigh-value + Pending:");
filterOrders(orders, highValuePending)
.forEach(o -> System.out.println(" " + o.orderId()
+ " | Rs." + o.amount() + " | " + o.status()));
}
}Output:
High-value orders (> Rs.1000):
ORD-001 | Rs.1499.0
ORD-003 | Rs.5999.0
ORD-004 | Rs.1800.0
Pending orders only:
ORD-001 | Rs.1499.0
ORD-003 | Rs.5999.0
ORD-005 | Rs.150.0
High-value + Pending:
ORD-001 | Rs.1499.0
ORD-003 | Rs.5999.0
The lambda (id, amt, st) -> amt > 1000.0 is a compact implementation of OrderFilter — the compiler infers that the lambda body implements accepts(). No anonymous class, no boilerplate. The and() default method demonstrates how functional interfaces can be composed — combining two filters into a new one without any new class.
Interface vs Abstract Class
| Aspect | Interface | Abstract Class |
|---|---|---|
| Keyword | interface | abstract class |
| Implemented / Extended by | implements | extends |
| Multiple inheritance | Yes — a class can implement many | No — a class can extend only one |
| Abstract methods | Yes — all methods are abstract by default | Yes — explicitly declared with abstract |
| Concrete methods | default and static only (Java 8+) | Yes — regular methods with bodies |
| Instance fields | No — only public static final constants | Yes — any field type |
| Constructors | No | Yes — called via super() in subclass |
| Access modifiers on methods | public only (by default) | Any — public, protected, package-private |
| State (mutable data) | No | Yes |
| IS-A relationship | Yes — type identity | Yes — type identity |
| When to use | Pure contract, multiple type inheritance, capability definition | Partial implementation shared across subclasses |
Interface Method Types — Java 8+
| Method Type | Syntax | Body | Inherited By | Called Via |
|---|---|---|---|---|
| Abstract | void method(); | None | Implementing class — must implement | Object reference |
| Default | default void method() {} | Yes | Implementing class — can override | Object reference |
| Static | static void method() {} | Yes | Not inherited | Interface name only |
| Private (Java 9+) | private void method() {} | Yes | Not inherited — helper for defaults | Other interface methods only |
Interface vs Interface with Default Methods — Before and After Java 8
| Aspect | Before Java 8 | Java 8+ |
|---|---|---|
| Method types | Abstract only | Abstract, default, static |
| Evolving an interface | Adding a method breaks all implementors | Add as default — no breakage |
| Code in interface | None | Default and static methods |
| Functional programming | Verbose anonymous classes | Lambdas with functional interfaces |
| Multiple implementation | Only abstract contracts | Contracts + shared behaviour |
Basic Example
public class Main {
interface Payable {
double calculatePay();
}
static class Employee implements Payable {
private final double monthlySalary;
Employee(double monthlySalary) { this.monthlySalary = monthlySalary; }
@Override public double calculatePay() { return monthlySalary; }
}
static class Freelancer implements Payable {
private final double hourlyRate;
private final int hoursWorked;
Freelancer(double hourlyRate, int hoursWorked) {
this.hourlyRate = hourlyRate;
this.hoursWorked = hoursWorked;
}
@Override public double calculatePay() { return hourlyRate * hoursWorked; }
}
public static void main(String[] args) {
Payable[] payables = {
new Employee(50000.0),
new Freelancer(500.0, 40)
};
for (Payable p : payables) {
System.out.println(p.getClass().getSimpleName() + " pay: " + p.calculatePay());
}
}
}Output
Employee pay: 50000.0 Freelancer pay: 20000.0
- ›
Payabledeclares one method,calculatePay(), with no body — it is a contract, not an implementation. - ›
EmployeeandFreelancereachimplements Payableand provide their own version ofcalculatePay(). - ›The loop holds each object only through the
Payablereference type, yet callingcalculatePay()runs the correct class's own logic. - ›Adding a new kind of payable worker later — say a
Contractor— needs no change to this loop, only a new class that implementsPayable.
Best Practices
Keep interfaces focused on a single capability — Interface Segregation Principle
An interface that combines location, pricing, rating, and scheduling into one forces every implementing class to implement all four — even if some are irrelevant. Four separate interfaces — Locatable, Priceable, Ratable, Schedulable — let each class implement only what it supports. This is the Interface Segregation Principle: no class should be forced to implement methods it does not use.
Name interfaces as capabilities or roles — not as class prefixes
Printable, Comparable, Runnable, Serializable, Iterable — Java's own standard library names interfaces as adjectives or capabilities. Avoid naming interfaces IPaymentGateway or AbstractPrintable — the I prefix and Abstract prefix are unnecessary and uninformative. Name the interface after what objects that implement it can do.
Use @FunctionalInterface when an interface is designed for lambdas
When you intend an interface to be used with lambda expressions, annotate it with @FunctionalInterface. This causes a compile error if anyone accidentally adds a second abstract method, protecting the functional contract. Java's built-in Predicate<T>, Function<T,R>, Consumer<T>, and Supplier<T> all use this annotation.
Prefer interfaces over abstract classes for type definitions
When choosing between an interface and an abstract class to define a type, prefer the interface unless the type genuinely needs shared state or shared concrete implementation logic. Interfaces impose fewer restrictions — implementing classes can still extend a class of their choice. Abstract classes consume the single inheritance slot. Give implementing classes maximum freedom by using interfaces.
Common Mistakes
Mistake 1 — Trying to Add State to an Interface
public interface Counter {
int count = 0; // implicitly public static final — this is a constant, not state
void increment();
int getCount();
}
// Every implementing class will share the same count = 0 constant
// Modifying count through an implementation does NOT change the interface's constant
// The implementing class needs its own instance field for actual stateInterface fields are constants — they cannot be used as instance state shared or modified by implementing objects. Every field in an interface is implicitly public static final. For mutable state that implementing classes need, each class must declare its own instance fields.
Mistake 2 — Implementing an Interface Without Providing All Abstract Methods
public interface Reportable {
String generateReport();
void saveReport(String path);
}
// Compile error — saveReport() not implemented
public class QuickReport implements Reportable {
@Override
public String generateReport() {
return "Quick report content";
}
// Missing saveReport() implementation — compiler error
}Compile error: QuickReport is not abstract and does not override abstract method saveReport(String) in Reportable
Every abstract method in every implemented interface must have an implementation in the concrete class. If the class cannot provide all implementations, it must be declared abstract itself — allowing a concrete subclass to complete the contract.
Mistake 3 — Default Method Conflict Without Resolution
public interface InterfaceA {
default void display() { System.out.println("InterfaceA display"); }
}
public interface InterfaceB {
default void display() { System.out.println("InterfaceB display"); }
}
// Compile error — which display() should be used?
public class ConflictDemo implements InterfaceA, InterfaceB {
// Must override display() to resolve the conflict
// Without override: "class ConflictDemo inherits unrelated defaults"
}// Correct resolution
public class ConflictDemo implements InterfaceA, InterfaceB {
@Override
public void display() {
InterfaceA.super.display(); // explicitly pick one — or write a new body
System.out.println("ConflictDemo resolves the conflict");
}
}When two interfaces provide default methods with the same signature, the implementing class must override the method and explicitly resolve which version to use — or provide an entirely new implementation. The compiler enforces this.
Mistake 4 — Using an Interface as a Constant Container
// Anti-pattern — constants-only interface (Constant Interface Anti-Pattern)
public interface AppConstants {
String BASE_URL = "https://api.example.com";
int TIMEOUT_MS = 5000;
String API_VERSION = "v2";
}
// Classes implement it just to use the constants without class-name prefix
public class PaymentService implements AppConstants {
public void callApi() {
System.out.println(BASE_URL + "/" + API_VERSION); // works but wrong
}
}Implementing an interface solely to avoid writing AppConstants.BASE_URL everywhere is an anti-pattern. It pollutes the class's type hierarchy — PaymentService is not an AppConstants. Use a public final class with public static final fields and a private constructor instead: AppConfig.BASE_URL.
Interview Questions
Q1. What is an interface in Java and how is it different from a class?
An interface is a reference type that defines a contract — a set of method signatures that implementing classes must provide. A class defines concrete types with state and behaviour. A class can implement multiple interfaces but extend only one class. Interfaces cannot be instantiated, cannot hold instance fields or constructors, and before Java 8 could only hold abstract methods. After Java 8, they can hold default and static methods. A class is a full implementation; an interface is a contract.
Q2. What are default methods in Java interfaces and why were they introduced?
Default methods are methods in an interface that have a body — implementing classes inherit them automatically without writing any code. They were introduced in Java 8 specifically to allow interfaces to evolve without breaking existing implementing classes. Before Java 8, adding any method to an interface forced every implementing class to add that method or fail to compile — impossible when classes exist in libraries you do not control. Default methods provide backwards compatibility for interface evolution. Implementing classes can override them when needed.
Q3. What is the difference between an interface and an abstract class in Java?
An interface defines a pure contract — only method signatures (and since Java 8, default and static methods). It has no instance fields, no constructors, and supports multiple implementation. An abstract class can have concrete methods, instance fields, and constructors, but a class can only extend one. Use an abstract class when subclasses share concrete implementation or state. Use an interface for a pure contract, for defining capability roles, or when multiple type inheritance is needed. Most Java design guidelines favour interfaces over abstract classes for type definitions.
Q4. What is a functional interface and how does it relate to lambda expressions?
A functional interface is an interface with exactly one abstract method. The @FunctionalInterface annotation enforces this constraint at compile time. Lambda expressions are a shorthand for implementing a functional interface — the lambda body implements the single abstract method. Java 8's standard functional interfaces (Predicate<T>, Function<T,R>, Consumer<T>, Supplier<T>) are all functional interfaces. Functional interfaces are the bridge between Java's object-oriented type system and functional programming style.
Q5. What happens when two interfaces that a class implements have a default method with the same signature?
The implementing class must override the conflicting method to resolve the ambiguity — the compiler enforces this with an error. Inside the overriding method, the class can call either interface's default using InterfaceA.super.method() or InterfaceB.super.method(), or provide an entirely new body. This is the diamond problem equivalent for interface default methods — Java resolves it by requiring the implementing class to make the choice explicit.
Q6. Can an interface extend another interface in Java?
Yes. An interface can extend one or more other interfaces using the extends keyword — and it can extend multiple interfaces simultaneously (unlike classes). The extending interface inherits all abstract methods of the parent interfaces and can add new ones. Any class implementing the child interface must implement all abstract methods from the entire inheritance chain. Interfaces cannot implement other interfaces — only extend them.
FAQs
What is an interface in Java?
An interface is a reference type that defines a contract — a set of abstract method signatures that implementing classes must provide. It cannot be instantiated, cannot hold mutable state, and since Java 8 can include default and static methods with bodies. A class declares it fulfils an interface using implements.
Can an interface have a constructor in Java?
No. Interfaces cannot have constructors because they cannot be instantiated. State initialisation happens in the implementing class's own constructor.
Can an interface have concrete methods?
Yes, since Java 8. default methods have a body and are inherited by implementing classes. static methods have a body and are called on the interface name. Since Java 9, interfaces can also have private methods — used as helpers within the interface's own default methods.
What is the difference between implements and extends in Java?
A class uses implements to fulfil an interface's contract — multiple interfaces are allowed. A class uses extends to inherit from another class — only one is allowed. An interface uses extends to inherit from other interfaces — multiple are allowed. These keywords convey the relationship type: implements is "I provide this contract" and extends is "I am a specific kind of this."
What is the Interface Segregation Principle?
The Interface Segregation Principle states that no class should be forced to implement methods it does not use. Large interfaces should be split into smaller, more specific ones. A class should implement only the interfaces relevant to its capabilities. This prevents classes from having empty or unsupported method implementations.
Can an interface extend a class in Java?
No. An interface cannot extend a class. An interface can only extend other interfaces. Classes can implement interfaces. This one-way relationship is enforced by the language.
Summary
Interfaces are Java's contracts — they define what objects can do without prescribing how. A class that implements an interface makes a binding promise to provide every abstract method the interface declares. Since Java 8, default methods allow interfaces to evolve and provide shared behaviour, and functional interfaces enable lambda expressions as concise implementations.
The practical power of interfaces is twofold: they allow multiple type inheritance — one class can fulfil many contracts simultaneously — and they decouple callers from implementations entirely. Code written against PaymentGateway works with Razorpay, PayU, Stripe, or any future gateway without a single change. That replaceability is what interfaces exist to provide.
For interviews, be ready to explain the difference between interfaces and abstract classes clearly, describe the purpose of default methods and when they were introduced, demonstrate how a class implements multiple interfaces, and define a functional interface with a lambda example. These points appear consistently in both service-based recall and product-based design discussions.
What to Read Next
Practice Programs
Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.
A single interface implemented by a single class.
A single class implementing two unrelated interfaces at once.
An interface extending another interface, adding its own extra method.
Multiple classes implementing a shared interface.
A static utility method defined directly on an interface, called through the interface name.
An interface with a single abstract method, implemented inline with a lambda.
Notifies every subscribed observer whenever a product's price changes, using the Observer design pattern.