Step 1 of 7 · Reading · ~3 min
Learn
Inheritance and Interfaces
Interfaces
An interface is a contract: a list of method signatures that any class promising to follow it must implement. By the end of this lesson you can define and implement an interface, give it a ready-made default method, and use a lambda wherever an interface has a single abstract method.
Defining and implementing
interface Check {
boolean test(String s); // no body: just the signature
}
static class NotEmpty implements Check {
@Override
public boolean test(String s) {
return !s.isEmpty();
}
}
static class MaxLen implements Check {
private final int max;
MaxLen(int max) {
this.max = max;
}
@Override
public boolean test(String s) {
return s.length() <= max;
}
}
A class that says implements Check must supply every method the interface declares, or the compiler refuses. Two details trip people up:
- Interface methods are implicitly
public, so the implementing method must be declaredpublictoo. Leaving it off gives "attempting to assign weaker access privileges". - The interface says what, never how. Different classes give different hows for the same what.
Code against the interface
The payoff comes when callers depend only on the interface:
Check[] rules = { new NotEmpty(), new MaxLen(5) };
for (Check rule : rules) {
System.out.println(rule.test("abcdefg")); // true, then false
}
The loop knows nothing about NotEmpty or MaxLen; it works for any class that implements Check, including ones written later. That is polymorphism through an interface, and it is how well-designed Java keeps parts replaceable and testable.
default methods
An interface can supply a method body marked default. Every implementor inherits it for free and may override it. A default method can call the interface's own abstract methods, because by then some real object is behind this:
interface Check {
boolean test(String s);
default Check and(Check other) {
return s -> test(s) && other.test(s);
}
}
and builds a new Check that passes only when both do. Now new NotEmpty().and(new MaxLen(5)) is itself a Check, and .and(...) chains again. Inside the lambda, test(s) is this object's check, and other.test(s) is the other one. Default methods exist so interfaces can grow without breaking classes that already implement them.
Lambdas: implementing an interface in one line
An interface with exactly one abstract method is a functional interface, and a lambda can stand in for a class that implements it:
Check noBang = s -> s.indexOf('!') < 0;
Check blank = String::isEmpty; // a method reference works too
System.out.println(noBang.test("hey!")); // false
This is what powers streams: filter takes a Predicate, map takes a Function, and both are functional interfaces from java.util.function.
Static members, multiple interfaces
An interface may hold static helper methods and constants (fields are implicitly public static final). A class may implement several interfaces, class P implements Named, Aged, which is Java's answer to multiple inheritance: many contracts, one parent class. If two interfaces supply conflicting default methods, the class must override the method and choose.
Interface or abstract class?
Use an interface for a capability that unrelated classes can share. Use an abstract class when subclasses share state and partial implementation under one parent. Interfaces cannot hold per-object state.
Common mistakes
- Implementing without
public. - Leaving out a method the interface declares.
- Calling a method through the interface type that exists only on the class. The variable's declared type decides what you may call.
- Putting state in an interface; its fields are constants.
Your exercise
Text formatters share one contract, and the program composes a chain of them in the order they are named. You write the formatter classes and the default method that joins two formatters into one. Order matters: applying A then B is not the same as B then A.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…