Interfaces
An interface defines a contract: a set of methods a type promises to provide, without dictating how. Interfaces are Java’s primary tool for decoupling—callers depend on the interface, and any conforming implementation can be swapped in. Since Java 8 they have grown well beyond pure abstraction, gaining default and static methods.
Declaring and Implementing
An interface declares method signatures; a class adopts the contract with implements and supplies bodies.
public interface Repository<T> {
void save(T entity);
java.util.Optional<T> findById(long id);
}
public class InMemoryUserRepo implements Repository<String> {
private final java.util.Map<Long, String> store = new java.util.HashMap<>();
@Override public void save(String entity) { store.put((long) entity.hashCode(), entity); }
@Override public java.util.Optional<String> findById(long id) {
return java.util.Optional.ofNullable(store.get(id));
}
}
Interface methods are implicitly public abstract; implementing methods must be public. Fields in an interface are implicitly public static final constants.
Default and Static Methods (Java 8)
A default method provides a body inside the interface, letting you add behavior without breaking existing implementors. A static method belongs to the interface itself and is called on its name.
public interface Greeter {
String name();
default String greet() { // evolvable behavior
return "Hello, " + name();
}
static Greeter of(String n) { // factory on the interface
return () -> n;
}
}
Greeter g = Greeter.of("DevCraftly");
System.out.println(g.greet());
Output:
Hello, DevCraftly
Note: Default methods exist mainly to evolve published interfaces (the JDK used them to add
Collection.stream()without breaking the ecosystem). Don’t overuse them to smuggle real logic into interfaces.
Functional Interfaces
A functional interface has exactly one abstract method, so it can be implemented by a lambda or method reference. Annotate it with @FunctionalInterface to have the compiler enforce that rule.
@FunctionalInterface
interface Transformer {
String apply(String input);
}
Transformer upper = String::toUpperCase;
System.out.println(upper.apply("scale"));
Output:
SCALE
The java.util.function package ships ready-made functional interfaces—Function, Predicate, Supplier, Consumer—that power streams and lambdas throughout modern Java.
Multiple Interface Implementation
A class can implement any number of interfaces, composing capabilities that no single class hierarchy could.
public class Document implements Comparable<Document>, AutoCloseable {
@Override public int compareTo(Document o) { return 0; }
@Override public void close() { /* release resources */ }
}
If two interfaces supply default methods with the same signature, the compiler forces the implementing class to override the method and resolve the conflict explicitly—often delegating with Interface.super.method().
interface A { default String id() { return "A"; } }
interface B { default String id() { return "B"; } }
class C implements A, B {
@Override public String id() { return A.super.id(); } // disambiguate
}
Warning: Without that explicit override, a default-method clash is a compile error. The language refuses to guess which one you meant.
Interface vs Abstract Class
| Aspect | Interface | Abstract Class |
|---|---|---|
| Multiple inheritance | Yes (many) | No (single) |
| Instance state | No (constants only) | Yes |
| Constructors | No | Yes |
| Method bodies | default/static/private | Concrete + abstract |
| Models | A capability (can-do) | An is-a with shared state |
| Lambda target | Yes (if functional) | No |
Best Practices
- Program to interfaces; declare variables and parameters by interface type.
- Keep interfaces small and cohesive—prefer several focused contracts over one fat interface.
- Use
@FunctionalInterfaceto lock in single-abstract-method intent. - Reserve default methods for interface evolution, not for hiding substantial logic.
- Reuse
java.util.functiontypes instead of inventing equivalents.
Interview Questions
Q: Can an interface have a constructor? A: No. Interfaces have no instance state to initialize, so they cannot declare constructors.
Q: How does Java resolve conflicting default methods from two interfaces?
A: It doesn’t—the implementing class must override the method explicitly, optionally delegating with InterfaceName.super.method().
Q: What makes an interface a functional interface?
A: Exactly one abstract method. Default, static, and Object-inherited methods don’t count, so the single abstract method can be supplied by a lambda or method reference.
Q: Since Java 8 added default methods, why still use abstract classes? A: Abstract classes can hold mutable instance state, declare constructors, and enforce a single shared base implementation—none of which interfaces support.