Default, Static and Private Methods in Interfaces
Suppose PaymentMethod is used by fifty classes, and now every payment needs a receipt(). Before Java 8, adding a method to an interface broke every class that implemented it. Default methods solved that: an interface method can now carry a body that implementing classes inherit, and override only if they want to. Java 8 also allowed static methods in interfaces, and Java 9 added private methods so that defaults can share helper code.
Four Kinds of Interface Method
public class Receipts {
interface PaymentMethod {
String name();
long fee(long amount);
default String receipt(String customer, long amount) {
return customer + " paid " + rupees(amount + fee(amount))
+ " by " + name() + feeNote(amount);
}
private String feeNote(long amount) {
long f = fee(amount);
return f == 0 ? "" : " (fee " + rupees(f) + ")";
}
static String rupees(long amount) { return "Rs. " + amount; }
}
static class Upi implements PaymentMethod {
public String name() { return "UPI"; }
public long fee(long amount) { return 0; }
}
static class Card implements PaymentMethod {
public String name() { return "card"; }
public long fee(long amount) { return amount * 2 / 100; }
}
static class NetBanking implements PaymentMethod {
public String name() { return "net banking"; }
public long fee(long amount) { return 10; }
@Override
public String receipt(String customer, long amount) {
String usual = PaymentMethod.super.receipt(customer, amount);
return "Bank ref 7731. " + usual;
}
}
public static void main(String[] args) {
System.out.println(new Upi().receipt("Navika", 1500));
System.out.println(new Card().receipt("Srihitha", 1500));
System.out.println(new NetBanking().receipt("Advitha", 1500));
System.out.println(PaymentMethod.rupees(99));
}
}Navika paid Rs. 1500 by UPI
Srihitha paid Rs. 1530 by card (fee Rs. 30)
Bank ref 7731. Advitha paid Rs. 1510 by net banking (fee Rs. 10)
Rs. 99How it works
name()andfee()are abstract: every class must implement them.default String receipt(...)has a body.UpiandCardinherit it unchanged. Inside, it calls the abstract methods, so each class's ownfeeandnameare used.NetBankingoverrides the default and reuses it withPaymentMethod.super.receipt(...), the syntax for calling an interface's default version.private String feeNoteis a helper shared by default methods. Implementing classes can't see or call it.static String rupeesbelongs to the interface and is called asPaymentMethod.rupees(99).
Try it yourself: add a Wallet class with a fee of Rs. 5, and print a receipt for Navika's Rs. 250 order.
The Rules
| Kind | Body? | Inherited by classes? | Call it as | Since |
|---|---|---|---|---|
| Abstract | No | Must be implemented | obj.name() | Java 1.0 |
default | Yes | Yes, can be overridden | obj.receipt(...) | Java 8 |
static | Yes | No | PaymentMethod.rupees(99) | Java 8 |
private | Yes | No, invisible outside | Only from the interface's own methods | Java 9 |
private static | Yes | No | From the interface's static or default methods | Java 9 |
Static interface methods aren't inherited, even by implementing classes. Upi.rupees(99) fails with cannot find symbol. This avoids ambiguity when a class implements two interfaces with the same static method.
Defaults Can't Override Object Methods
interface Greeter {
default String toString() { return "Greeter"; }
}E4.java:2: error: default method toString in interface Greeter overrides a member of java.lang.Object
default String toString() { return "Greeter"; }
^
1 errorEvery class already inherits toString, equals and hashCode from Object, and class methods always win over interface defaults. A default version could never run, so Java forbids it.
Defaults in the JDK
Java 8 used defaults to add features to old interfaces without breaking millions of classes:
| Interface | Default method added |
|---|---|
Iterable | forEach(action) |
Collection | stream(), removeIf(filter) |
List | sort(comparator), replaceAll(op) |
Map | getOrDefault, putIfAbsent, merge |
Comparator | reversed(), thenComparing(...) |
Common Mistakes
Calling a static interface method through a class or object. Use the interface name.
Using super.receipt() in the class. For interface defaults, write InterfaceName.super.receipt().
Trying to keep state in a default method. Interfaces have no instance fields; defaults can only use the abstract methods and parameters.
Interview Corner
Why were default methods added to interfaces in Java 8?
To let interfaces evolve without breaking existing implementations. Adding an abstract method forces every implementing class to change, but a default method provides a body that classes inherit automatically. Java 8 used this to add methods such as forEach, stream and sort to collection interfaces.
What is the difference between default and static methods in an interface?
Default methods are instance methods with a body; implementing classes inherit them and can override them, and they're called on an object. Static methods belong to the interface itself, aren't inherited by implementing classes or subinterfaces, can't be overridden, and are called as InterfaceName.method().
Why does Java 9 allow private methods in interfaces?
So default and static methods can share helper code without exposing it. Before Java 9, a shared helper had to be a public default or static method, which became part of the interface's public contract.
Practice
- Easy: Add a default method
boolean isFree(long amount)toPaymentMethodthat returnstruewhen the fee is zero. Call it on each class. - Harder: Add
static PaymentMethod of(String code)that returns aUpi,CardorNetBankingobject for the codes"UPI","CARD"and"NB", and use it inmain.
Key Takeaways
Was this tutorial helpful?