Interfaces and multiple behaviors
Inheritance, interfaces and polymorphism11 min readLesson 5 of 6
5.1 A contract for behavior
An interface declares methods that implementing classes must provide. The methods are public and abstract by default, so those keywords are optional. A class uses implements and writes the bodies.
An interface cannot hold instance fields, and it cannot be instantiated. It can declare constants, which are public, static, and final. It describes a capability that a class can have.
The implementing method must be public. The compiler rejects a package-private method that tries to implement a public interface method, because that would reduce visibility.
public class Main {
public static void main(String[] args) {
Playable player = new Piano();
player.play();
}
}
interface Playable {
void play();
}
class Piano implements Playable {
@Override
public void play() { System.out.println("Piano keys"); }
}Output
Piano keys
5.1.1 Several interfaces at once
A class can implement more than one interface. class SmartPhone implements Phone, Browser promises both sets of methods. This gives multiple behaviors without multiple inheritance of state.
If two interfaces declare the same method, one implementation satisfies both. If they declare default methods with the same signature, the class must override the method to resolve the conflict.
Use interfaces for capabilities such as Comparable and Runnable. A class can implement several of them at once.
public class Main {
public static void main(String[] args) {
SmartPhone phone = new SmartPhone();
phone.call();
phone.browse();
}
}
interface Phone {
void call();
}
interface Browser {
void browse();
}
class SmartPhone implements Phone, Browser {
public void call() { System.out.println("Calling"); }
public void browse() { System.out.println("Browsing"); }
}Output
Calling Browsing
5.1.2 Default and static methods
A default method provides a body in the interface. Implementing classes inherit it and can override it. Default methods let an interface add behavior without breaking every existing implementation.
A static method belongs to the interface and is called through its name, as in Greeter.loud("hi"). It cannot be overridden by an implementing class.
An interface can also have private methods for shared code used by its default methods. Those helpers stay hidden from implementing classes.
public class Main {
public static void main(String[] args) {
Greeter greeter = new Friendly();
greeter.greet("Asha");
System.out.println(Greeter.loud("hi"));
}
}
interface Greeter {
default void greet(String name) {
System.out.println("Hello, " + name);
}
static String loud(String text) {
return text.toUpperCase();
}
}
class Friendly implements Greeter { }Output
Hello, Asha HI
5.1.3 Interfaces and lambdas
An interface with exactly one abstract method is a functional interface. Runnable and Comparator are common examples. Java can implement such an interface with a lambda expression, which the modern Java lesson covers in more detail.
Formatter upper = text -> text.toUpperCase(); creates an implementation without a named class. The lambda's parameter type and return type come from the interface method.
The @FunctionalInterface annotation is optional and asks the compiler to check the single-method rule. It catches an accidental second abstract method.
public class Main {
public static void main(String[] args) {
Formatter upper = text -> text.toUpperCase();
System.out.println(upper.format("java"));
}
}
interface Formatter {
String format(String text);
}Output
JAVA
5.1.4 Choosing an interface
Use an interface when unrelated classes should share a capability, or when a class needs several independent contracts. It lets code depend on a capability that unrelated classes can supply.
Use an abstract class when subclasses share state and a partial implementation. A class can extend one abstract class and implement several interfaces, which is a common combination.
Keep interfaces small. A broad interface forces every implementer to supply methods it may not need, and a change to the interface touches every implementation.
public class Main {
public static void main(String[] args) {
Shape[] shapes = { new Circle(), new Square() };
for (Shape shape : shapes) {
System.out.println(shape.area());
}
}
}
interface Shape {
double area();
}
class Circle implements Shape {
public double area() { return 3.0; }
}
class Square implements Shape {
public double area() { return 4.0; }
}Output
3.0 4.0
5.1.5 Practice
- Declare
Playablewithplay()and implement it in two classes. - Add a second interface
Recordablewithrecord()and implement both in one class. - Add a default method to one interface and call it on an implementation.
- Add a static helper to the interface and call it through the interface name.
- Create a
Formatterfunctional interface and implement it with a lambda.
For step 2, check that the class provides a public method for every abstract method in both interfaces.
