Interfaces (default and static methods)

A fest ticket and a canteen order have nothing in common as classes, yet both can be paid for. An interface lets unrelated classes promise the same ability, and a class can make many such promises at once. You'll see how interfaces work as contracts, what Java 8's default and static methods and Java 9's private methods add, how the diamond rule works, when to choose an interface over an abstract class, and then practise with 10 problems.

Module 7 · OOP Pillars⏱ 25 minIntermediate10 practice problems
1 The story

Two parents? Not allowed. Two promises? Fine.

In the fest app, Ticket extends Event and CanteenOrder extends FoodItem. Both must go through the same payment screen. Riya tries class Ticket extends Event, Payable and javac stops her: a class can have only one parent class (Lesson 7.2).

EventFoodItem TicketCanteenOrder extendsextends «interface» Payable implements Pay screen pay(Payable p) myphatshala.in
Riya

Sir, Ticket already extends Event. How can it also be "payable" like a canteen order?

Arjun Sir

Make Payable an interface: a list of methods every payable thing promises to have. A class extends one parent but can implement as many interfaces as it likes.

Riya

And the payment screen?

Arjun Sir

It accepts a Payable. It doesn't care whether it's a ticket or a dosa, only that it can tell you the amount. That's abstraction at its cleanest.

2 Definition

What is an interface?

Formal definition (write this in exams)

An interface is a reference type, declared with the interface keyword, that defines a contract: a set of method signatures that implementing classes must provide. A class implements one or more interfaces with the implements keyword. Interface methods are implicitly public abstract unless declared default (Java 8, with a body), static (Java 8) or private (Java 9). Fields are implicitly public static final constants. An interface cannot be instantiated and has no constructors or instance fields.

In simple words

An interface is a job description: "anyone in this role must be able to do X and Y". A class can hold several roles at once, whatever family it comes from.

Words you'll meet in this lesson

implementsA class promising to provide an interface's methods.
Default methodAn interface method with a body (Java 8); implementing classes inherit it.
Static methodA utility method belonging to the interface: Payable.withGst(500).
Private methodA helper shared by default methods, hidden from implementers (Java 9).
X.super.m()Calls interface X's default m() from an implementing class.
Functional interfaceAn interface with exactly one abstract method; can be implemented by a lambda.

What an interface can contain

Abstract methodsdouble amount(); is implicitly public abstract. Implementations must be public.
Constantsint MAX_ITEMS = 10; is implicitly public static final
Default methodsJava 8+: default String receipt() { … }, inherited and overridable
Static methodsJava 8+: called only as Payable.withGst(x); not inherited by implementing classes
Private methodsJava 9+: helpers for default/static methods; invisible outside
Not allowedConstructors, instance fields, new Payable() ("Payable is abstract; cannot be instantiated")

Exam tip: a class extends one class and implements many interfaces, and extends comes first: class Ticket extends Event implements Payable, Notifiable. An interface extends other interfaces (possibly several). Forgetting public on an implementing method gives "attempting to assign weaker access privileges; was public".

3 Types and ways to use it

The kinds of interface members

Abstract methods

The contract. Every implementing class must write them.

must implement

default methods

Ready-made behaviour built on the contract. Lets an interface grow without breaking old classes.

Java 8

static and private

Utilities called on the interface itself; private helpers that remove duplication.

Java 8 / 9

Every kind of member in one program

interface Payable {
    double GST_RATE = 0.18;                          // constant: public static final

    double amount();                                 // abstract: the contract

    default String receipt() {                       // default: inherited behaviour
        return line("Amount", amount()) + "\n" + line("With GST", withGst(amount()));
    }

    static double withGst(double a) {                // static: a utility
        return a * (1 + GST_RATE);
    }

    private String line(String label, double value) { // private helper (Java 9)
        return String.format("%-9s Rs %.2f", label, value);
    }
}

class Ticket implements Payable {
    @Override
    public double amount() { return 500; }           // must be public
}

public class Main {
    public static void main(String[] args) {
        Payable p = new Ticket();
        System.out.println(p.receipt());
        System.out.println("Static call: " + Payable.withGst(1000));
    }
}
Amount Rs 500.00 With GST Rs 590.00 Static call: 1180.0

Interface or abstract class?

InterfaceAbstract class
A class can haveMany (implements A, B, C)One (extends)
Instance fields (state)No; only constantsYes
ConstructorsNoYes
Method bodiesdefault, static, private (Java 8/9+)Any concrete methods
Member accessPublic (except private helpers)Any access level
ModelsA capability ("can be paid", "can be tracked")A family with shared state ("is a vehicle")
Typical useContracts between parts of a system, plug-insShared base code and template methods

The diamond rule for defaults: if a class implements two interfaces that both provide a default label(), javac refuses: "class Ticket inherits unrelated defaults for label() from types Payable and Notifiable". The class must override label(), and may call a specific one with Payable.super.label(). Also: a method inherited from a class always wins over an interface default ("class wins").

4 Step by step

Designing with interfaces

  1. Write the contract

    interface Payable {
        double amount();
    }
    javac compiles an interface to a .class file marked as an interface (and abstract). amount() is stored as public abstract even though you didn't type those words.
  2. Implement it in any class, from any family

    class Ticket extends Event implements Payable, Notifiable {
        public double amount() { return price; }
        public String contact() { return phone; }
    }
    A Ticket object is now an Event, a Payable and a Notifiable at once: instanceof is true for all three, and it can be stored in a variable of any of those types.
  3. Program to the interface

    double total(Payable[] items) {
        double sum = 0;
        for (Payable p : items) sum += p.amount();
        return sum;
    }
    Calls through an interface type use invokeinterface; the JVM finds the implementation in the object's class, just like dynamic dispatch with classes.
  4. Add behaviour later with a default method

    default String receipt() { return "Receipt: Rs " + amount(); }
    Before Java 8, adding a method to an interface broke every implementing class. A default method gives old classes a working implementation automatically; that's how List.sort and forEach were added to existing collections.
  5. Resolve clashes explicitly

    @Override
    public String label() {
        return Payable.super.label() + " + " + Notifiable.super.label();
    }
    X.super.m() works only for an interface the class directly implements. It lets the class reuse either (or both) default versions after javac forces it to decide.
5 See it happen

One class, several promises: both views

Left: the code and its result. Right: the class, its parent class (solid line) and its interfaces (dashed lines).

Ticket implements Payable, Notifiable 6 steps
What you see Payable p = new Ticket(); Payable q = new Payable(); System.out.println(p.receipt()); Payable.withGst(500) (both interfaces: default label()) Payable.super.label() p.amount() → 500.0 Receipt: Rs 500.0 590.0 p.label() → payable error: Payable is abstract; cannot be instantiated error: types Payable and Notifiable are incompatible; class Ticket inherits unrelated defaults for label() from both An interface type holds any implementer. An interface is only a contract. Ticket never wrote receipt(); it inherited it. Static: called on the interface name. Two defaults, same name: javac stops. Override and pick one with X.super. myphatshala.in
Behind the screen Event(class) Payable amount() default receipt() static withGst() Notifiable default label() default label() Ticket amount() { 500 } label() { P.super… } ? One parent class, any number of interfaces. No constructor, no fields: nothing to create. receipt() runs with this = the Ticket. Static methods stay with the interface. Same default from two sides: ambiguous. The class's own method beats both defaults. myphatshala.in
6 Real-world use

Interfaces in real systems

Interfaces are the most common way large Java systems connect their parts.

Watch it with an example: one tracking page for parcels and food orders

A courier parcel and a food order come from unrelated classes, but both implement Trackable. Watch the tracking page use the interface's abstract, default and static methods, and settle a default-method clash.

Trackable: one contract, unrelated classes 6 steps
Animated example Trackable t = new Parcel(); t = new FoodOrder(); t.summary(); // default Trackable.isValidAwb("AWB12345") class Parcel implements Trackable, Insurable label() { return Trackable.super.label(); } «interface» Trackable String status(); default summary() static isValidAwb() default label() Parcel FoodOrder Insurable t.status() → In transit t.status() → Out for delivery FoodOrder: Out for delivery true (called on the interface) error: types Trackable and Insurable are incompatible; Parcel inherits unrelated defaults for label() p.label() → tracked An interface type can hold any implementing object. An unrelated class keeps the same promise. The default method runs on the FoodOrder. Static methods belong to the interface itself. Two defaults with the same name: javac stops. Override and pick one with X.super. myphatshala.in
ListArrayListLinkedList

1. The Collections Framework

List, Set and Map are interfaces; ArrayList, HashSet and HashMap implement them. Code written against List works with any implementation (Module 11).

List<Order>→implemented by ArrayList→swap without changes

2. Swappable payment and SMS providers

A PaymentGateway or SmsSender interface hides which vendor is used. Switching providers, or using a fake one in tests, means adding a class, not rewriting checkout.

Checkout→gateway.charge(order)→any provider
list.sort((a, b) -> …);// Comparator

3. Lambdas and functional interfaces

Comparator, Runnable and Predicate have one abstract method each, so a short lambda can implement them: sorting orders by price is one line (Lesson 14.1).

Comparator→(a, b) -> a.price - b.price→sorted list
ComparableAutoCloseableSerializable

4. Capabilities the JDK understands

Implementing Comparable lets objects be sorted; AutoCloseable lets them be used in try-with-resources (Lesson 9.6). Your class gains abilities by making promises.

implements AutoCloseable→try (res) { … }→closed automatically

Examples are simplified. Frameworks for dependency injection rely heavily on interfaces so implementations can be configured rather than hard-coded.

7 Run the code

One checkout for unrelated things

interface Payable {
    double amount();

    default String receipt() {
        return getClass().getSimpleName() + ": Rs " + amount();
    }
}

interface Notifiable {
    String contact();
}

class Event {
    final String title;
    Event(String title) { this.title = title; }
}

class Ticket extends Event implements Payable, Notifiable {
    Ticket(String title) { super(title); }

    @Override public double amount() { return 499; }
    @Override public String contact() { return "riya@college.in"; }
}

class CanteenOrder implements Payable {
    private final int plates;
    CanteenOrder(int plates) { this.plates = plates; }

    @Override public double amount() { return plates * 60; }
}

public class Main {
    public static void main(String[] args) {
        Payable[] cart = { new Ticket("Tech Fest"), new CanteenOrder(3) };
        double total = 0;
        for (Payable p : cart) {
            System.out.println(p.receipt());
            total += p.amount();
            if (p instanceof Notifiable n) {
                System.out.println("  confirmation to " + n.contact());
            }
        }
        System.out.println("Total: Rs " + total);
    }
}
Ticket: Rs 499.0 confirmation to riya@college.in CanteenOrder: Rs 180.0 Total: Rs 679.0
  • extends Event implements Payable, NotifiableOne parent class, two interfaces; extends comes first.
  • public double amount()Interface methods are public, so implementations must be too.
  • default String receipt()Both classes get it for free; it calls each object's own amount().
  • p instanceof Notifiable nOnly some payables can be notified; pattern matching checks safely (Lesson 7.5).
8 Line by line

A default method calling the object's own method

Watch the frame for Payable.receipt, code that lives in the interface, call Ticket.amount.

Worked examples: real problems, solved and traced in memory

Three business problems solved with interfaces. Run each solution, then step through it: watch interface-typed variables point at objects of unrelated classes, default methods run with this set to the implementing object, and a lambda appear on the heap as an object.

Example 1Fest app · checkout

Unrelated classes, one Payable total

Problem statement

A ticket and a canteen order come from different class families, but checkout must add up both. Use a Payable interface so the total loop works for anything payable.

Input
Ticket Rs 499; CanteenOrder 2 plates × Rs 60
Expected output
Total: Rs 619.0

Approach

  1. Declare interface Payable { double amount(); }.
  2. Ticket and CanteenOrder each implement it in their own way.
  3. Store both in a Payable[] and sum amount().

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2Notifications · alerts

Two default footers, one decision

Problem statement

An order alert goes out by SMS and email. Both channel interfaces provide a default footer(), so the alert class must decide. It should use the SMS footer followed by the email footer.

Input
new OrderAlert().footer()
Expected output
Reply STOP to opt out | Unsubscribe link below

Approach

  1. SmsChannel and EmailChannel each have default String footer().
  2. OrderAlert implements both, so it must override footer().
  3. Inside, call SmsChannel.super.footer() and EmailChannel.super.footer().

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3E-commerce · offers

A discount rule written as a lambda

Problem statement

The pricing team keeps changing festival offers. Instead of a new class each time, define a functional interface Offer and write today's offer (20% off) as a lambda. Apply it to a Rs 1,500 cart and a Rs 4,000 bulk order.

Input
carts Rs 1500 and Rs 4000, offer 20% off
Expected output
Pay: Rs 1200.0, bulk: Rs 3200.0

Approach

  1. @FunctionalInterface interface Offer { double apply(double total); } has one abstract method.
  2. Write the offer as t -> t * 0.8; Java creates an object implementing Offer.
  3. Call apply like any interface method and watch the lambda's code run.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 7.7 in one screen

  • Interface = contract; a class implements any number of interfaces (after its one extends).
  • Abstract methods are implicitly public abstract; implementations must be public.
  • Fields are constants: public static final. No constructors, no instance fields, no new.
  • Java 8: default methods (inherited, overridable) and static methods (call as Interface.m()).
  • Java 9: private helper methods inside the interface.
  • Two clashing defaults → the class must override; reuse one with X.super.m(). A class's method beats a default.
  • Interface = capability, many per class; abstract class = family with shared state, one per class. One abstract method = functional interface = lambda-friendly.

Interview and viva questions

What is the difference between an interface and an abstract class?

A class can implement many interfaces but extend only one class. Interfaces have no instance fields or constructors (only constants), and their members are public apart from private helpers; abstract classes can have state, constructors and any access level. Use an interface for a capability or contract that unrelated classes share; use an abstract class to share state and code within a family. Since Java 8, both can contain method bodies.

Why were default methods added in Java 8?

To let interfaces evolve without breaking existing implementations. Adding an abstract method to Collection would have broken every collection class ever written; adding default void forEach(...) and default Stream stream() gave all of them working implementations automatically.

How does Java solve the diamond problem with default methods?

Rules in order: (1) a method from a class or superclass wins over any interface default; (2) a more specific interface (a sub-interface) wins over its parent; (3) otherwise, if two unrelated interfaces provide the same default, the class must override the method, and can call a particular one with InterfaceName.super.method().

Can an interface extend another interface? Can it extend a class?

An interface can extend one or more interfaces: interface SmartCard extends Payable, Trackable. It cannot extend a class. A class implementing the sub-interface must implement the methods of all of them.

What is a marker interface?

An interface with no methods, used only to mark a class as having some property, such as Serializable or Cloneable. Code checks it with instanceof. Newer code often uses annotations for the same purpose.

Practice problems

In each problem, first write the interface (the promise), then the classes that keep it. Remember that implementing methods must be public. The last three are business problems.

P1Easy

A Printable contract

Write an interface Printable with String print(). Implement it in Invoice and MarkSheet, two unrelated classes, and print both through a Printable[].

Input
Invoice #INV-77 Rs 2360; MarkSheet "Riya" 86%
Expected output
Invoice INV-77: Rs 2360
Mark sheet: Riya, 86%
Show solution and explanation
interface Printable {
    String print();
}

class Invoice implements Printable {
    @Override
    public String print() { return "Invoice INV-77: Rs 2360"; }
}

class MarkSheet implements Printable {
    @Override
    public String print() { return "Mark sheet: Riya, 86%"; }
}

public class Main {
    public static void main(String[] args) {
        Printable[] queue = { new Invoice(), new MarkSheet() };
        for (Printable p : queue) {
            System.out.println(p.print());
        }
    }
}
Invoice INV-77: Rs 2360 Mark sheet: Riya, 86%

How it works: Invoice and MarkSheet share no parent class, but both promise print(). The printer queue only needs that promise, so it uses the interface type.

P2Easy

Implementing two interfaces

A SmartPhone is both a Camera (with click()) and a Phone (with call(String)). Implement both and use the object through each interface type.

Input
call "98200 11111", click a photo
Expected output
Calling 98200 11111
Photo saved
Same object? true
Show solution and explanation
interface Camera {
    String click();
}

interface Phone {
    String call(String number);
}

class SmartPhone implements Camera, Phone {
    @Override
    public String click() { return "Photo saved"; }

    @Override
    public String call(String number) { return "Calling " + number; }
}

public class Main {
    public static void main(String[] args) {
        SmartPhone s = new SmartPhone();
        Phone p = s;
        Camera c = s;
        System.out.println(p.call("98200 11111"));
        System.out.println(c.click());
        System.out.println("Same object? " + (p == c));
    }
}
Calling 98200 11111 Photo saved Same object? true

How it works: one object, two roles. Through p only call is visible, through c only click, but both variables point to the same SmartPhone.

P3Easy

Interface constants

Put the GST rate and the free-delivery limit as constants in an interface ShopRules. Use them from a class to compute the bill for a Rs 450 order (delivery Rs 40 below Rs 499).

Input
order Rs 450
Expected output
Order: 450.0
GST: 22.5
Delivery: 40
Total: 512.5
Show solution and explanation
interface ShopRules {
    double GST = 0.05;
    int FREE_DELIVERY_ABOVE = 499;
    int DELIVERY_FEE = 40;
}

public class Main {
    public static void main(String[] args) {
        double order = 450;
        double gst = order * ShopRules.GST;
        int delivery = order >= ShopRules.FREE_DELIVERY_ABOVE ? 0 : ShopRules.DELIVERY_FEE;
        System.out.println("Order: " + order);
        System.out.println("GST: " + gst);
        System.out.println("Delivery: " + delivery);
        System.out.println("Total: " + (order + gst + delivery));
    }
}
Order: 450.0 GST: 22.5 Delivery: 40 Total: 512.5

How it works: interface fields are implicitly public static final, so they're read as ShopRules.GST and can't be changed. (For large sets of constants, a final class or an enum is usually better; see the scenarios.)

P4Medium

A default method on top of the contract

Interface Rateable has abstract int[] ratings() and a default average() computed from them. Implement it for a Restaurant; it should get average() without writing it.

Input
ratings 5, 4, 4, 3, 5
Expected output
Average rating: 4.2
Show solution and explanation
interface Rateable {
    int[] ratings();

    default double average() {
        int[] r = ratings();
        if (r.length == 0) return 0;
        int sum = 0;
        for (int x : r) sum += x;
        return (double) sum / r.length;
    }
}

class Restaurant implements Rateable {
    @Override
    public int[] ratings() { return new int[]{5, 4, 4, 3, 5}; }
}

public class Main {
    public static void main(String[] args) {
        Rateable r = new Restaurant();
        System.out.println("Average rating: " + r.average());
    }
}
Average rating: 4.2

How it works: the default method is written once in the interface and uses the abstract ratings(), which dispatches to Restaurant. 21 / 5 = 4.2.

P5Medium

Static helper in an interface

Add a static method isValidPin(String) to an interface AddressCheck that returns true for a 6-digit Indian PIN code not starting with 0. Check "411001", "041001" and "4110".

Input
"411001", "041001", "4110"
Expected output
411001 valid? true
041001 valid? false
4110 valid? false
Show solution and explanation
interface AddressCheck {
    static boolean isValidPin(String pin) {
        if (pin.length() != 6 || pin.charAt(0) == '0') return false;
        for (char c : pin.toCharArray()) {
            if (!Character.isDigit(c)) return false;
        }
        return true;
    }
}

public class Main {
    public static void main(String[] args) {
        String[] pins = {"411001", "041001", "4110"};
        for (String p : pins) {
            System.out.println(p + " valid? " + AddressCheck.isValidPin(p));
        }
    }
}
411001 valid? true 041001 valid? false 4110 valid? false

How it works: static interface methods are utilities called on the interface name. They aren't inherited by implementing classes, so there's no confusion about which version runs.

P6Medium

Resolve a default-method clash

Wifi and Bluetooth both have default String connect(). A Laptop implements both. Override connect() so it uses both defaults, one after the other.

Input
new Laptop().connect()
Expected output
Wi-Fi connected, Bluetooth paired
Show solution and explanation
interface Wifi {
    default String connect() { return "Wi-Fi connected"; }
}

interface Bluetooth {
    default String connect() { return "Bluetooth paired"; }
}

class Laptop implements Wifi, Bluetooth {
    @Override
    public String connect() {
        return Wifi.super.connect() + ", " + Bluetooth.super.connect();
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new Laptop().connect());
    }
}
Wi-Fi connected, Bluetooth paired

How it works: without the override, javac reports that Laptop "inherits unrelated defaults for connect()". Wifi.super.connect() calls a specific interface's default; this syntax works only for interfaces the class directly implements.

P7Medium

Functional interface with lambdas

Declare @FunctionalInterface interface TaxRule { double tax(double amount); }. Create rules for 5% and 18% GST with lambdas, and a rule for 0% on exports. Apply each to Rs 2,000.

Input
amount Rs 2000
Expected output
food: Rs 100.0
electronics: Rs 360.0
export: Rs 0.0
Show solution and explanation
@FunctionalInterface
interface TaxRule {
    double tax(double amount);
}

public class Main {
    public static void main(String[] args) {
        TaxRule food = amt -> amt * 0.05;
        TaxRule electronics = amt -> amt * 0.18;
        TaxRule export = amt -> 0;
        TaxRule[] rules = { food, electronics, export };
        String[] names = { "food", "electronics", "export" };
        for (int i = 0; i < rules.length; i++) {
            System.out.println(names[i] + ": Rs " + rules[i].tax(2000));
        }
    }
}
food: Rs 100.0 electronics: Rs 360.0 export: Rs 0.0

How it works: an interface with exactly one abstract method can be implemented by a lambda: amt -> amt * 0.05 is a short way of writing a class with a tax method. @FunctionalInterface makes javac check there is only one. Lambdas are covered fully in Module 14.

P8Business problem

Payment gateways behind one interface

Checkout must work with any gateway. Define PaymentGateway with boolean charge(double amount) and a default String name(). Implement RazorLikeGateway (fails above Rs 1,00,000) and BankGateway (always succeeds). Route orders above Rs 50,000 to the bank gateway.

Input
orders Rs 1,499, Rs 75,000
Expected output
Rs 1499.0 via RazorLikeGateway: paid
Rs 75000.0 via BankGateway: paid
Show solution and explanation
interface PaymentGateway {
    boolean charge(double amount);

    default String name() {
        return getClass().getSimpleName();
    }
}

class RazorLikeGateway implements PaymentGateway {
    @Override
    public boolean charge(double amount) {
        return amount <= 100000;
    }
}

class BankGateway implements PaymentGateway {
    @Override
    public boolean charge(double amount) {
        return true;
    }
}

public class Main {
    static PaymentGateway route(double amount) {
        return amount > 50000 ? new BankGateway() : new RazorLikeGateway();
    }

    public static void main(String[] args) {
        double[] orders = {1499, 75000};
        for (double amt : orders) {
            PaymentGateway g = route(amt);
            System.out.println("Rs " + amt + " via " + g.name() + ": " + (g.charge(amt) ? "paid" : "failed"));
        }
    }
}
Rs 1499.0 via RazorLikeGateway: paid Rs 75000.0 via BankGateway: paid

How it works:

  1. Checkout depends only on PaymentGateway; the router decides which implementation to use.
  2. The default name() gives every gateway a label without extra code; any gateway can override it.
  3. Adding a third gateway changes only route and adds one class. (Gateway names here are invented for teaching.)
P9Business problem

Trackable shipments of different kinds

Parcels, freight containers and food orders are unrelated classes, but customers can track all of them. Define Trackable with String status() and int etaMinutes(), plus a default String summary(). Print a tracking page for one of each.

Input
Parcel (in transit, 180 min), Container (at port, 4320 min), FoodOrder (out for delivery, 12 min)
Expected output
Parcel     In transit         ETA 3 hour(s)
Container  At port, Nhava Sheva ETA 3 day(s)
FoodOrder  Out for delivery   ETA 12 min
Show solution and explanation
interface Trackable {
    String status();
    int etaMinutes();

    default String summary() {
        int m = etaMinutes();
        String eta = m >= 1440 ? (m / 1440) + " day(s)" : m >= 60 ? (m / 60) + " hour(s)" : m + " min";
        return String.format("%-10s %-18s ETA %s", getClass().getSimpleName(), status(), eta);
    }
}

class Parcel implements Trackable {
    public String status() { return "In transit"; }
    public int etaMinutes() { return 180; }
}

class Container implements Trackable {
    public String status() { return "At port, Nhava Sheva"; }
    public int etaMinutes() { return 4320; }
}

class FoodOrder implements Trackable {
    public String status() { return "Out for delivery"; }
    public int etaMinutes() { return 12; }
}

public class Main {
    public static void main(String[] args) {
        Trackable[] items = { new Parcel(), new Container(), new FoodOrder() };
        for (Trackable t : items) {
            System.out.println(t.summary());
        }
    }
}
Parcel In transit ETA 3 hour(s) Container At port, Nhava Sheva ETA 3 day(s) FoodOrder Out for delivery ETA 12 min

How it works:

  1. The three classes could each extend different parents; the interface still lets one tracking page handle them.
  2. summary() is a default method built only from the contract methods, so every implementer gets consistent formatting.
  3. 4320 minutes = 3 days; 180 minutes = 3 hours.
P10Business problem

Employee benefits from several interfaces

Some employees get insurance, some get a fuel allowance, some get both. Model Insured (double premiumCover()) and FuelEligible (double fuelAllowance()) as interfaces. A SalesManager implements both, a Developer only Insured. Print each person's benefits using instanceof.

Input
SalesManager "Kiran"; Developer "Neha"
Expected output
Kiran: insurance Rs 500000.0, fuel Rs 6000.0/month
Neha: insurance Rs 300000.0
Show solution and explanation
interface Insured {
    double premiumCover();
}

interface FuelEligible {
    double fuelAllowance();
}

abstract class Employee {
    final String name;
    Employee(String name) { this.name = name; }
}

class SalesManager extends Employee implements Insured, FuelEligible {
    SalesManager(String name) { super(name); }
    public double premiumCover() { return 500000; }
    public double fuelAllowance() { return 6000; }
}

class Developer extends Employee implements Insured {
    Developer(String name) { super(name); }
    public double premiumCover() { return 300000; }
}

public class Main {
    public static void main(String[] args) {
        Employee[] staff = { new SalesManager("Kiran"), new Developer("Neha") };
        for (Employee e : staff) {
            StringBuilder sb = new StringBuilder(e.name + ":");
            if (e instanceof Insured i) sb.append(" insurance Rs ").append(i.premiumCover());
            if (e instanceof FuelEligible f) sb.append(", fuel Rs ").append(f.fuelAllowance()).append("/month");
            System.out.println(sb);
        }
    }
}
Kiran: insurance Rs 500000.0, fuel Rs 6000.0/month Neha: insurance Rs 300000.0

How it works:

  1. Benefits are capabilities that cut across job roles, so they're interfaces; the job hierarchy stays a single abstract class.
  2. A class extends one class and implements as many interfaces as apply.
  3. Pattern matching checks each capability independently, so a new benefit is a new interface, not a new branch of the class tree.

Real-world scenario questions (asked in company interviews)

Interfaces decide how easy a system is to change and to test. Decide your answer first, then open the model answer.

Payments · vendor switchFresher

Changing the payment gateway in a weekend

An e-commerce app calls a specific gateway's classes directly in 30 places. The business wants to move to a cheaper gateway, and maybe use both depending on the amount. How should the code have been designed?

The interviewer is checking: do you know how to program to an interface?

Show a strong answer
  • Define interface PaymentGateway { PaymentResult charge(Order o); Refund refund(String txnId); }.
  • Wrap each vendor in its own implementing class. Checkout depends only on PaymentGateway.
  • A small routing class can choose an implementation per order (amount, method), and tests can use a fake gateway.

Avoid: replacing vendor A's classes with vendor B's in 30 places. That's the same mistake again.

Library · versioning1–3 years

Adding a method to a published interface

Your team owns an SDK interface Shipment implemented by 15 partner logistics companies. You need a new method estimatedDelivery(). Adding it as an abstract method would break every partner's build. What do you do?

The interviewer is checking: do you know why default methods exist?

Show a strong answer
  • Add it as a default method with a reasonable fallback (for example, dispatch date + standard days), so existing implementations keep compiling.
  • Document it and let partners override it with accurate estimates.
  • In the next major version, consider making it abstract with advance notice.

Avoid: putting real business state in the interface. Defaults can only use other interface methods, not fields.

Notifications · merge conflict1–3 years

Two libraries, one default method name

Your OrderAlert class implements SmsChannel from one library and EmailChannel from another. After an upgrade, both interfaces added default String footer(), and your build fails. How do you fix it, and what does the error tell you?

The interviewer is checking: do you know the default-method conflict rule?

Show a strong answer
  • javac says OrderAlert "inherits unrelated defaults for footer()". It won't pick one silently.
  • Override footer() in OrderAlert and decide: reuse one with SmsChannel.super.footer(), combine both, or write your own.
  • Add a test that checks the footer, since the upgrade changed behaviour you rely on.

Avoid: downgrading the library forever to dodge the conflict.

SaaS · design review3+ years

Constants interface everywhere

A codebase has interface AppConstants { int MAX_RETRIES = 3; String GST_STATE = "MH"; … } with 200 constants, and many classes implements AppConstants just to use the names without a prefix. A reviewer calls it an anti-pattern. Why, and what would you use instead?

The interviewer is checking: do you understand that interfaces define types, not storage?

Show a strong answer
  • Implementing an interface makes a class a subtype of it. "OrderService is an AppConstants" is meaningless, leaks into the public API and every subclass.
  • Put constants in a final class with a private constructor, grouped by topic, or in enums (Lesson 10.4), and use import static for short names.
  • Values that change by environment (GST state, retry counts) belong in configuration files, not code.

Avoid: using inheritance just to save typing a prefix.