Java Tutorial
🔍

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 public by 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

Java
1// File: Printable.java 2 3public interface Printable { 4 5 // Abstract method — no body, implementing class must provide one 6 void print(); 7 8 // Constant — implicitly public static final 9 int MAX_PRINT_WIDTH = 120; 10}

Interface with Default and Static Methods (Java 8+)

Java
1// File: Auditable.java 2 3public interface Auditable { 4 5 // Abstract — must be implemented 6 String getEntityId(); 7 String getEntityType(); 8 9 // Default method — has a body, implementing class inherits it automatically 10 default String buildAuditLog(String action) { 11 return "[AUDIT] " + getEntityType() + " | ID: " + getEntityId() 12 + " | Action: " + action 13 + " | At: " + java.time.LocalDateTime.now(); 14 } 15 16 // Static method — belongs to the interface, called on interface name 17 static String formatTimestamp(java.time.LocalDateTime dt) { 18 return dt.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); 19 } 20}

Interface Extending Multiple Interfaces

Java
1// File: TrackableEntity.java 2 3public interface TrackableEntity extends Printable, Auditable { 4 5 // Adds methods to the combined contract 6 String getStatus(); 7 8 default String getFullSummary() { 9 return getEntityType() + " [" + getEntityId() + "] — " + getStatus(); 10 } 11}

Implementing an Interface

Java
1// File: Order.java 2 3public class Order implements TrackableEntity { 4 5 private final String orderId; 6 private final String customerName; 7 private String status; 8 9 public Order(String orderId, String customerName) { 10 this.orderId = orderId; 11 this.customerName = customerName; 12 this.status = "PLACED"; 13 } 14 15 // Required by Auditable 16 @Override 17 public String getEntityId() { return orderId; } 18 19 @Override 20 public String getEntityType() { return "Order"; } 21 22 // Required by TrackableEntity 23 @Override 24 public String getStatus() { return status; } 25 26 // Required by Printable 27 @Override 28 public void print() { 29 System.out.println("Order: " + orderId 30 + " | Customer: " + customerName 31 + " | Status: " + status); 32 } 33 34 public void updateStatus(String newStatus) { 35 System.out.println(buildAuditLog("STATUS_UPDATE → " + newStatus)); 36 this.status = newStatus; 37 } 38}
Java
1// File: InterfaceBasicDemo.java 2 3public class InterfaceBasicDemo { 4 5 public static void main(String[] args) { 6 7 Order order = new Order("ORD-4521", "Priya Mehta"); 8 9 // Polymorphic references — same object through different interface lenses 10 Printable printable = order; 11 Auditable auditable = order; 12 TrackableEntity trackable = order; 13 14 order.print(); // Printable 15 System.out.println(auditable.buildAuditLog("VIEWED")); // default method 16 System.out.println(trackable.getFullSummary()); // default method 17 18 order.updateStatus("SHIPPED"); 19 order.print(); 20 } 21}
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.

Java
1// File: DeliveryAgent.java 2 3public interface DeliveryAgent { 4 5 String getAgentId(); 6 String getAgentName(); 7 double getCurrentLatitude(); 8 double getCurrentLongitude(); 9 10 // Default — available to all implementations without writing it 11 default String getLocationSummary() { 12 return getAgentName() + " | Location: (" 13 + getCurrentLatitude() + ", " 14 + getCurrentLongitude() + ")"; 15 } 16 17 default boolean isWithinRadius(double targetLat, double targetLng, 18 double radiusKm) { 19 double latDiff = Math.abs(getCurrentLatitude() - targetLat); 20 double lngDiff = Math.abs(getCurrentLongitude() - targetLng); 21 double approxDistKm = Math.sqrt(latDiff * latDiff + lngDiff * lngDiff) * 111; 22 return approxDistKm <= radiusKm; 23 } 24 25 // Static — utility method on the interface itself 26 static String formatCoordinates(double lat, double lng) { 27 return String.format("(%.4f, %.4f)", lat, lng); 28 } 29}
Java
1// File: BikeAgent.java 2 3public class BikeAgent implements DeliveryAgent { 4 5 private final String agentId; 6 private final String agentName; 7 private double latitude; 8 private double longitude; 9 private String vehicleNumber; 10 11 public BikeAgent(String agentId, String agentName, 12 double latitude, double longitude, String vehicleNumber) { 13 this.agentId = agentId; 14 this.agentName = agentName; 15 this.latitude = latitude; 16 this.longitude = longitude; 17 this.vehicleNumber = vehicleNumber; 18 } 19 20 @Override public String getAgentId() { return agentId; } 21 @Override public String getAgentName() { return agentName; } 22 @Override public double getCurrentLatitude() { return latitude; } 23 @Override public double getCurrentLongitude() { return longitude; } 24 25 public void updateLocation(double lat, double lng) { 26 this.latitude = lat; 27 this.longitude = lng; 28 } 29 30 // Optional — overrides the default only if customisation is needed 31 @Override 32 public String getLocationSummary() { 33 return DeliveryAgent.super.getLocationSummary() // calls interface default 34 + " | Vehicle: " + vehicleNumber; 35 } 36}
Java
1// File: DefaultMethodDemo.java 2 3public class DefaultMethodDemo { 4 5 public static void main(String[] args) { 6 7 BikeAgent agent = new BikeAgent( 8 "AGT-001", "Rahul Kumar", 9 12.9716, 77.5946, "KA05AB1234" // Bengaluru coordinates 10 ); 11 12 System.out.println(agent.getLocationSummary()); // overridden default 13 14 // Interface static method — called on interface name 15 System.out.println(DeliveryAgent.formatCoordinates( 16 agent.getCurrentLatitude(), agent.getCurrentLongitude())); 17 18 // Check if agent is within 5 km of delivery point 19 double deliveryLat = 12.9352; 20 double deliveryLng = 77.6245; 21 System.out.println("Within 5 km of delivery: " 22 + agent.isWithinRadius(deliveryLat, deliveryLng, 5.0)); 23 } 24}
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.

Java
1// File: Identifiable.java 2 3public interface Identifiable { 4 String getId(); 5}
Java
1// File: Priceable.java 2 3public interface Priceable { 4 double getPrice(); 5 default double getPriceWithGst() { return getPrice() * 1.18; } 6}
Java
1// File: Searchable.java 2 3public interface Searchable { 4 String getSearchKeywords(); 5 default boolean matchesQuery(String query) { 6 return getSearchKeywords().toLowerCase().contains(query.toLowerCase()); 7 } 8}
Java
1// File: Product.java 2 3// One class — three interface contracts fulfilled simultaneously 4public class Product implements Identifiable, Priceable, Searchable { 5 6 private final String productId; 7 private final String productName; 8 private final String category; 9 private final double price; 10 11 public Product(String productId, String productName, 12 String category, double price) { 13 this.productId = productId; 14 this.productName = productName; 15 this.category = category; 16 this.price = price; 17 } 18 19 @Override public String getId() { return productId; } 20 @Override public double getPrice() { return price; } 21 22 @Override 23 public String getSearchKeywords() { 24 return productName + " " + category + " " + productId; 25 } 26 27 public String getName() { return productName; } 28 public String getCategory() { return category; } 29 30 public String getSummary() { 31 return productId + " | " + productName + " | Rs." + price; 32 } 33}
Java
1// File: MultipleInterfaceDemo.java 2 3import java.util.List; 4 5public class MultipleInterfaceDemo { 6 7 // Each method accepts a specific interface — Product satisfies all three 8 public static void displayId(Identifiable item) { 9 System.out.println("ID: " + item.getId()); 10 } 11 12 public static void displayPrice(Priceable item) { 13 System.out.printf("Price: Rs.%.2f | With GST: Rs.%.2f%n", 14 item.getPrice(), item.getPriceWithGst()); 15 } 16 17 public static void searchProducts(List<Searchable> items, String query) { 18 System.out.println("Searching for: \"" + query + "\""); 19 items.stream() 20 .filter(item -> item.matchesQuery(query)) 21 .forEach(item -> System.out.println(" Found: " + item.getSearchKeywords())); 22 } 23 24 public static void main(String[] args) { 25 26 Product laptop = new Product("SKU-001", "Laptop Pro 15", "Electronics", 85000.0); 27 Product mouse = new Product("SKU-002", "Wireless Mouse", "Electronics", 1299.0); 28 Product notebook = new Product("SKU-003", "A4 Notebook 200pg","Stationery", 89.0); 29 30 // Same object passed to methods expecting different interface types 31 displayId(laptop); 32 displayPrice(laptop); 33 System.out.println(); 34 35 displayId(mouse); 36 displayPrice(mouse); 37 System.out.println(); 38 39 searchProducts(List.of(laptop, mouse, notebook), "electronics"); 40 System.out.println(); 41 searchProducts(List.of(laptop, mouse, notebook), "laptop"); 42 } 43}
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.

Java
1// File: OrderFilter.java 2 3@FunctionalInterface 4public interface OrderFilter { 5 6 // Exactly one abstract method — what makes this a functional interface 7 boolean accepts(String orderId, double amount, String status); 8 9 // Default methods are allowed — they don't count as abstract 10 default OrderFilter and(OrderFilter other) { 11 return (id, amt, st) -> this.accepts(id, amt, st) && other.accepts(id, amt, st); 12 } 13}
Java
1// File: FunctionalInterfaceDemo.java 2 3import java.util.List; 4 5public class FunctionalInterfaceDemo { 6 7 record OrderRecord(String orderId, double amount, String status) {} 8 9 public static List<OrderRecord> filterOrders(List<OrderRecord> orders, 10 OrderFilter filter) { 11 return orders.stream() 12 .filter(o -> filter.accepts(o.orderId(), o.amount(), o.status())) 13 .toList(); 14 } 15 16 public static void main(String[] args) { 17 18 List<OrderRecord> orders = List.of( 19 new OrderRecord("ORD-001", 1499.0, "PENDING"), 20 new OrderRecord("ORD-002", 299.0, "SHIPPED"), 21 new OrderRecord("ORD-003", 5999.0, "PENDING"), 22 new OrderRecord("ORD-004", 1800.0, "DELIVERED"), 23 new OrderRecord("ORD-005", 150.0, "PENDING") 24 ); 25 26 // Lambda implements OrderFilter — no class needed 27 OrderFilter highValue = (id, amt, st) -> amt > 1000.0; 28 OrderFilter pendingOnly = (id, amt, st) -> "PENDING".equals(st); 29 30 // Compose two filters using the default and() method 31 OrderFilter highValuePending = highValue.and(pendingOnly); 32 33 System.out.println("High-value orders (> Rs.1000):"); 34 filterOrders(orders, highValue) 35 .forEach(o -> System.out.println(" " + o.orderId() + " | Rs." + o.amount())); 36 37 System.out.println("\nPending orders only:"); 38 filterOrders(orders, pendingOnly) 39 .forEach(o -> System.out.println(" " + o.orderId() + " | " + o.status())); 40 41 System.out.println("\nHigh-value + Pending:"); 42 filterOrders(orders, highValuePending) 43 .forEach(o -> System.out.println(" " + o.orderId() 44 + " | Rs." + o.amount() + " | " + o.status())); 45 } 46}
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

AspectInterfaceAbstract Class
Keywordinterfaceabstract class
Implemented / Extended byimplementsextends
Multiple inheritanceYes — a class can implement manyNo — a class can extend only one
Abstract methodsYes — all methods are abstract by defaultYes — explicitly declared with abstract
Concrete methodsdefault and static only (Java 8+)Yes — regular methods with bodies
Instance fieldsNo — only public static final constantsYes — any field type
ConstructorsNoYes — called via super() in subclass
Access modifiers on methodspublic only (by default)Any — public, protected, package-private
State (mutable data)NoYes
IS-A relationshipYes — type identityYes — type identity
When to usePure contract, multiple type inheritance, capability definitionPartial implementation shared across subclasses

Interface Method Types — Java 8+

Method TypeSyntaxBodyInherited ByCalled Via
Abstractvoid method();NoneImplementing class — must implementObject reference
Defaultdefault void method() {}YesImplementing class — can overrideObject reference
Staticstatic void method() {}YesNot inheritedInterface name only
Private (Java 9+)private void method() {}YesNot inherited — helper for defaultsOther interface methods only

Interface vs Interface with Default Methods — Before and After Java 8

AspectBefore Java 8Java 8+
Method typesAbstract onlyAbstract, default, static
Evolving an interfaceAdding a method breaks all implementorsAdd as default — no breakage
Code in interfaceNoneDefault and static methods
Functional programmingVerbose anonymous classesLambdas with functional interfaces
Multiple implementationOnly abstract contractsContracts + shared behaviour

Real-World Example — Ride-Booking Platform

The Business Problem

You are building the core domain model for a ride-booking platform — similar to what Ola or Rapido runs for its driver and vehicle management. The system has multiple vehicle types — Auto, Bike, Cab — each of which can be located, priced, rated, and in some cases, booked for scheduled rides. Each capability is modelled as a separate interface. A vehicle can implement the interfaces relevant to its capabilities — not all vehicles support all features.

Implementation

Java
1// File: Locatable.java 2 3public interface Locatable { 4 double getLatitude(); 5 double getLongitude(); 6 7 default double distanceTo(double targetLat, double targetLng) { 8 double latDiff = Math.abs(getLatitude() - targetLat); 9 double lngDiff = Math.abs(getLongitude() - targetLng); 10 return Math.sqrt(latDiff * latDiff + lngDiff * lngDiff) * 111.0; 11 } 12}
Java
1// File: Priceable.java 2 3public interface Priceable { 4 double getBaseRatePerKm(); 5 double calculateFare(double distanceKm); 6}
Java
1// File: Ratable.java 2 3public interface Ratable { 4 double getRating(); 5 int getTotalRatings(); 6 7 default String getRatingDisplay() { 8 return String.format("%.1f (%d ratings)", getRating(), getTotalRatings()); 9 } 10}
Java
1// File: Schedulable.java 2 3public interface Schedulable { 4 boolean acceptsScheduledBooking(); 5 int getMinAdvanceBookingMinutes(); 6}
Java
1// File: AutoRickshaw.java 2 3// Auto implements Locatable, Priceable, Ratable — but NOT Schedulable (autos don't do scheduled rides) 4public class AutoRickshaw implements Locatable, Priceable, Ratable { 5 6 private final String vehicleId; 7 private final String driverName; 8 private double latitude; 9 private double longitude; 10 private double rating; 11 private int totalRatings; 12 13 public AutoRickshaw(String vehicleId, String driverName, 14 double latitude, double longitude, 15 double rating, int totalRatings) { 16 this.vehicleId = vehicleId; 17 this.driverName = driverName; 18 this.latitude = latitude; 19 this.longitude = longitude; 20 this.rating = rating; 21 this.totalRatings = totalRatings; 22 } 23 24 @Override public double getLatitude() { return latitude; } 25 @Override public double getLongitude() { return longitude; } 26 @Override public double getRating() { return rating; } 27 @Override public int getTotalRatings(){ return totalRatings; } 28 29 @Override 30 public double getBaseRatePerKm() { return 14.0; } // Rs.14/km 31 32 @Override 33 public double calculateFare(double distanceKm) { 34 double base = 25.0; // minimum fare 35 return Math.max(base, distanceKm * getBaseRatePerKm()); 36 } 37 38 public String getSummary() { 39 return "Auto [" + vehicleId + "] | Driver: " + driverName 40 + " | Rating: " + getRatingDisplay(); 41 } 42}
Java
1// File: PremiumCab.java 2 3// PremiumCab implements all four interfaces — cabs support scheduled rides 4public class PremiumCab implements Locatable, Priceable, Ratable, Schedulable { 5 6 private final String vehicleId; 7 private final String driverName; 8 private final String carModel; 9 private double latitude; 10 private double longitude; 11 private double rating; 12 private int totalRatings; 13 14 public PremiumCab(String vehicleId, String driverName, String carModel, 15 double latitude, double longitude, 16 double rating, int totalRatings) { 17 this.vehicleId = vehicleId; 18 this.driverName = driverName; 19 this.carModel = carModel; 20 this.latitude = latitude; 21 this.longitude = longitude; 22 this.rating = rating; 23 this.totalRatings = totalRatings; 24 } 25 26 @Override public double getLatitude() { return latitude; } 27 @Override public double getLongitude() { return longitude; } 28 @Override public double getRating() { return rating; } 29 @Override public int getTotalRatings(){ return totalRatings; } 30 @Override public boolean acceptsScheduledBooking() { return true; } 31 @Override public int getMinAdvanceBookingMinutes(){ return 30; } 32 33 @Override 34 public double getBaseRatePerKm() { return 22.0; } // Rs.22/km 35 36 @Override 37 public double calculateFare(double distanceKm) { 38 double base = 50.0; 39 return Math.max(base, distanceKm * getBaseRatePerKm()); 40 } 41 42 public String getSummary() { 43 return "Cab [" + vehicleId + "] " + carModel 44 + " | Driver: " + driverName 45 + " | Rating: " + getRatingDisplay(); 46 } 47}
Java
1// File: RideBookingDemo.java 2 3import java.util.List; 4 5public class RideBookingDemo { 6 7 // Accepts any Locatable — vehicle-agnostic 8 public static void showNearbyVehicles(List<Locatable> vehicles, 9 double userLat, double userLng) { 10 System.out.println("Vehicles near (" + userLat + ", " + userLng + "):"); 11 for (Locatable vehicle : vehicles) { 12 double dist = vehicle.distanceTo(userLat, userLng); 13 System.out.printf(" Distance: %.2f km%n", dist); 14 } 15 } 16 17 // Accepts any Priceable — calculates fare regardless of vehicle type 18 public static void showFareEstimates(List<Priceable> vehicles, double distanceKm) { 19 System.out.println("Fare estimates for " + distanceKm + " km:"); 20 for (Priceable vehicle : vehicles) { 21 System.out.printf(" Rs.%.2f (Rs.%.2f/km)%n", 22 vehicle.calculateFare(distanceKm), vehicle.getBaseRatePerKm()); 23 } 24 } 25 26 public static void main(String[] args) { 27 28 AutoRickshaw auto = new AutoRickshaw( 29 "AUTO-042", "Ramesh Yadav", 30 12.9716, 77.5946, 4.3, 187 31 ); 32 33 PremiumCab cab = new PremiumCab( 34 "CAB-117", "Suresh Kumar", "Toyota Etios", 35 12.9800, 77.5900, 4.7, 642 36 ); 37 38 System.out.println("=== Vehicle Listings ===\n"); 39 System.out.println(auto.getSummary()); 40 System.out.println(cab.getSummary()); 41 42 System.out.println(); 43 showNearbyVehicles(List.of(auto, cab), 12.9352, 77.6245); 44 45 System.out.println(); 46 showFareEstimates(List.of(auto, cab), 8.5); 47 48 System.out.println(); 49 System.out.println("=== Scheduled Booking Eligibility ==="); 50 51 // Only PremiumCab implements Schedulable 52 List<Object> allVehicles = List.of(auto, cab); 53 for (Object vehicle : allVehicles) { 54 if (vehicle instanceof Schedulable schedulable) { 55 System.out.println(vehicle.getClass().getSimpleName() 56 + " accepts scheduled bookings. Min advance: " 57 + schedulable.getMinAdvanceBookingMinutes() + " minutes."); 58 } else { 59 System.out.println(vehicle.getClass().getSimpleName() 60 + " does not support scheduled bookings."); 61 } 62 } 63 } 64}
Output:
=== Vehicle Listings ===

Auto [AUTO-042] | Driver: Ramesh Yadav | Rating: 4.3 (187 ratings)
Cab [CAB-117] Toyota Etios | Driver: Suresh Kumar | Rating: 4.7 (642 ratings)

Vehicles near (12.9352, 77.6245):
  Distance: 5.07 km
  Distance: 5.40 km

Fare estimates for 8.5 km:
  Rs.119.00 (Rs.14.00/km)
  Rs.187.00 (Rs.22.00/km)

=== Scheduled Booking Eligibility ===
AutoRickshaw does not support scheduled bookings.
PremiumCab accepts scheduled bookings. Min advance: 30 minutes.

Each vehicle implements only the interfaces that match its capabilities. The booking system can ask "is this vehicle schedulable?" through instanceof Schedulable without knowing any specific vehicle type. Fare calculation, proximity, and rating display all work through their respective interface references — the concrete vehicle type is invisible to the calling code.

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

Java
1public interface Counter { 2 3 int count = 0; // implicitly public static final — this is a constant, not state 4 5 void increment(); 6 int getCount(); 7} 8 9// Every implementing class will share the same count = 0 constant 10// Modifying count through an implementation does NOT change the interface's constant 11// The implementing class needs its own instance field for actual state

Interface 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

Java
1public interface Reportable { 2 String generateReport(); 3 void saveReport(String path); 4} 5 6// Compile error — saveReport() not implemented 7public class QuickReport implements Reportable { 8 @Override 9 public String generateReport() { 10 return "Quick report content"; 11 } 12 // Missing saveReport() implementation — compiler error 13}
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

Java
1public interface InterfaceA { 2 default void display() { System.out.println("InterfaceA display"); } 3} 4 5public interface InterfaceB { 6 default void display() { System.out.println("InterfaceB display"); } 7} 8 9// Compile error — which display() should be used? 10public class ConflictDemo implements InterfaceA, InterfaceB { 11 // Must override display() to resolve the conflict 12 // Without override: "class ConflictDemo inherits unrelated defaults" 13}
Java
1// Correct resolution 2public class ConflictDemo implements InterfaceA, InterfaceB { 3 @Override 4 public void display() { 5 InterfaceA.super.display(); // explicitly pick one — or write a new body 6 System.out.println("ConflictDemo resolves the conflict"); 7 } 8}

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

Java
1// Anti-pattern — constants-only interface (Constant Interface Anti-Pattern) 2public interface AppConstants { 3 String BASE_URL = "https://api.example.com"; 4 int TIMEOUT_MS = 5000; 5 String API_VERSION = "v2"; 6} 7 8// Classes implement it just to use the constants without class-name prefix 9public class PaymentService implements AppConstants { 10 public void callApi() { 11 System.out.println(BASE_URL + "/" + API_VERSION); // works but wrong 12 } 13}

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

TopicLink
How abstract classes combine interface-like contracts with shared concrete implementationJava Abstract Classes →
How polymorphism uses interface references for runtime method dispatchJava Polymorphism →
How functional interfaces connect to lambda expressions and the Stream APIJava Lambda Expressions →
How abstraction uses interfaces to decouple callers from implementationsJava Abstraction →
How the default keyword works in Java switch expressionsJava switch Statement →
Java Interfaces | DevStackFlow