Every new version of Java differs from those that came before. Here's an example of changes in material that we've covered: before Java 5, the language didn't have
enum
s.
Similarly, Java 8 differs markedly from Java 7. Most of our lessons were written for the 7th version of the language, but of course we won't ignore important innovations.
Since we're already talking about interfaces in this lesson, we'll consider one update — default methods in interfaces.
You already know that an interface doesn't implement behavior. Its task is to describe the behavior that all the objects that implement it must have.
But developers frequently encountered situations where a method's implementation is the same in all classes.
Let's consider our old car example:
public interface Car {
public void gas();
public void brake();
}
public class Sedan implements Car {
@Override
public void gas() {
System.out.println("Gas!");
}
@Override
public void brake() {
System.out.println("Brake!");
}
}
public class Truck implements Car {
@Override
public void gas() {
System.out.println("Gas!");
}
@Override
public void brake() {
System.out.println("Brake!");
}
}
public class F1Car implements Car {
@Override
public void gas() {
System.out.println("Gas!");
}
@Override
public void brake() {
System.out.println("Brake!");
}
}
"In your opinion, what's the main problem with this code?
You probably noticed that we wrote a bunch of repeated code! This problem is common in programming, and you need to avoid it.
It's another matter that particular solutions didn't exist before Java 8 was released. With this version came the ability to specify default methods and implement them right inside the interface!
Here's how you do that:
public interface Car {
public default void gas() {
System.out.println("Gas!");
}
public default void brake() {
System.out.println("Brake!");
}
}
public class Sedan implements Car {
}
public class Truck implements Car {
}
public class F1Car implements Car {
}
Now the gas()
and brake()
methods, which were the same for all cars, have been moved to the interface. No repeated code is needed. What's more, the methods are available in each class!
public class Main {
public static void main(String[] args) {
F1Car f1Car = new F1Car();
Sedan sedan = new Sedan();
Truck truck = new Truck();
truck.gas();
sedan.gas();
f1Car.brake();
}
}
What if there are 100 classes with the gas()
method, but only 99 of them have the same behavior? Does that ruin everything and make the default method unfit for this situation?
Of course, not :) Default methods in interfaces can be overridden in the same way as ordinary ones.
public class UnusualCar implements Car {
@Override
public void gas() {
System.out.println("This car accelerates differently!");
}
@Override
public void brake() {
System.out.println("This car decelerates differently!");
}
}
All the 99 other types of cars will implement the default method, and the UnusualCar
class, which is an exception, won't spoil the overall picture and calmly defines its own behavior.
Multiple inheritance of interfaces.
As you already know, Java doesn't support multiple inheritance. There are many reasons for this. We'll look at them in detail in a separate lesson.
Other languages, such as C++, do support it. Without multiple inheritance, a serious problem arises: one object can have several different characteristics and 'behaviors'.
Here's an example from life: we are children to our parents, students to our teachers, and patients to our doctors. In life, we take on different roles and, accordingly, behave differently: obviously, we wouldn't speak with teachers the same way we speak to our close friends.
Let's try to translate this into code. Imagine that we have two classes: Pond and Aviary. For the pond, we need water fowl; for the aviary, we need flying birds.
To do this, we've created two base classes: FlyingBird
and Waterfowl
.
public class Waterfowl {
}
public class FlyingBird {
}
Accordingly, we'll send birds whose classes inherit FlyingBird
to the aviary, and we'll send birds that inherit Waterfowl
to the pond.
It all seems very simple.
But where do we send a duck?
It swims and flies. And we don't have multiple inheritance.
Fortunately, Java supports multiple implementation of interfaces. Though a class can't inherit several parents, it can easily implement several interfaces!
Our duck can be both a flying bird and a waterfowl :) We simply need to make FlyingBird
and Waterfowl
interfaces rather than classes to achieve the desired result.
public class Duck implements FlyingBird, Waterfowl {
// The methods of both interfaces can be easily combined into one class
@Override
public void fly() {
System.out.println("Fly!");
}
@Override
public void swim() {
System.out.println("Swim!");
}
}
Accordingly, our program retains the flexibility of classes, and, in combination with default methods, our ability to define objects' behavior becomes almost unlimited! :)
GO TO FULL VERSION