Implementing Interfaces
Interfaces define a contract — what an object can do — without dictating how it is done. Mastering interface rules is essential for the OCP exam and for writing flexible, maintainable code.
Interface vs Abstract Class
Both can be extended/implemented, but they serve different design goals.
| Feature | Interface | Abstract Class |
|---|---|---|
| State (instance fields) | No (only static final) |
Yes |
| Constructors | No | Yes |
| Multiple inheritance | Yes — a class can implement many | No — extends is single only |
| Default access for members | public (implicitly) |
Depends on declared modifier |
default methods |
Yes | Not applicable |
static methods |
Yes (not inherited) | Yes (inherited) |
private methods (Java 9+) |
Yes | Yes |
| Design intent | "Can do" relationship | "Is a" relationship with shared code |
Rule of thumb: prefer interfaces for type definitions. Use an abstract class when you need to share concrete state or constructors.
Default Method Conflict Resolution
When a class inherits the same default method from multiple interfaces, Java applies two rules.
Rule 1 — Classes win over interfaces
A method defined in a class always takes priority over a default method from an interface:
javainterface Greeter {
default String greet() { return "Hello from interface"; }
}
class Base {
public String greet() { return "Hello from class"; }
}
class MyClass extends Base implements Greeter {
// Base.greet() wins — no ambiguity
}
new MyClass().greet(); // "Hello from class"Rule 2 — More specific interface wins
If two interfaces share a default method and one interface extends the other, the more specific (child) interface wins:
javainterface A {
default String name() { return "A"; }
}
interface B extends A {
default String name() { return "B"; }
}
class C implements A, B {
// B is more specific — B.name() is chosen automatically
}
new C().name(); // "B"No winner? You must override
If neither rule resolves the conflict, the compiler forces you to override:
javainterface Left { default String value() { return "left"; } }
interface Right { default String value() { return "right"; } }
class Both implements Left, Right {
@Override
public String value() {
return Left.super.value(); // explicit delegation is allowed
}
}Interface Static Methods Are Not Inherited
Static methods on an interface must be called via the interface name — they are not available through implementing classes or subinterfaces:
javainterface MathUtil {
static int square(int n) { return n * n; }
}
MathUtil.square(5); // OK — 25
// SomeImpl.square(5); // Compile error — not inheritedComparable and Comparator
These two interfaces appear together on the exam.
Comparable — natural ordering
javapublic class Student implements Comparable<Student> {
private final String name;
private final double gpa;
public Student(String name, double gpa) {
this.name = name;
this.gpa = gpa;
}
@Override
public int compareTo(Student other) {
// negative = this is less, 0 = equal, positive = this is greater
return Double.compare(this.gpa, other.gpa);
}
}
List<Student> list = new ArrayList<>(List.of(
new Student("Alice", 3.8),
new Student("Bob", 3.5)
));
Collections.sort(list); // uses compareToComparator — external, flexible ordering
javaComparator<Student> byName = Comparator.comparing(s -> s.name); Comparator<Student> byGpaDesc = Comparator.comparingDouble( (Student s) -> s.gpa).reversed(); list.sort(byName.thenComparing(byGpaDesc));
Comparable |
Comparator |
|
|---|---|---|
| Location | Inside the class | Outside (lambda / separate class) |
| Method | compareTo(T o) |
compare(T o1, T o2) |
| Use | Natural order | Custom / multiple orderings |
Collections.sort |
Works directly | Pass as second argument |
Functional Interfaces and Default Methods
Interfaces with exactly one abstract method are functional interfaces and can be used as lambda targets. Default and static methods do not count toward the abstract-method limit:
java@FunctionalInterface
interface Transformer<T> {
T transform(T input); // the one abstract method
default Transformer<T> andThen(Transformer<T> after) {
return input -> after.transform(this.transform(input));
}
}
Transformer<String> upper = String::toUpperCase;
Transformer<String> trim = String::trim;
Transformer<String> both = trim.andThen(upper);
both.transform(" hello "); // "HELLO"Key Rules to Remember
- A class can implement multiple interfaces but extend only one class
defaultmethods provide implementations; a class override beats them- More-specific interface wins in diamond conflicts; equal specificity requires manual override
- Interface
staticmethods are called on the interface type — never inherited Comparable.compareToreturns negative/zero/positive;Comparator.comparedoes the same but from outside@FunctionalInterfaceis optional but communicates intent and triggers a compile error if violated