1. Abilities
To better understand the benefits of interfaces and where to use them, we need to talk about some more abstract things.
A class usually models a particular object. An interface corresponds less to objects, and more to their abilities or roles.
For example, things like cars, bikes, motorcycles, and wheels are best represented as classes and objects. But their abilities — such as "I can be ridden", "I can transport people", "I can stand" — are better presented as interfaces. Here are some examples:
Code | Description |
---|---|
|
Corresponds to the ability to move |
|
Corresponds to the ability to be ridden |
|
Corresponds to the ability to transport stuff |
|
The Wheel class can move |
|
The Car class can move, be ridden, and transport stuff |
|
The Skateboard class can move and be ridden |
2. Roles
Interfaces greatly simplify a programmer's life. Very often, a program has thousands of objects, hundreds of classes, but just a couple dozen interfaces, i.e. roles. There are few roles, but there are many ways to combine them (classes).
The whole point is that you don't have to write code for in each class to interact with every other class. You just need to interact with their roles (interfaces).
Imagine you are a pet trainer. Each of the pets you work with can have several different abilities. You get into a friendly argument with your neighbor regarding whose pets can make the most noise. To settle the matter, you just line up all the pets that can "speak", and you give them the command: Speak!
You don't care what kind of animal they are or what other abilities they have. Even if they can do a triple back somersault. At this particular moment, you are only interested in their ability to speak loudly. Here's what it would look like in code:
Code | Description |
---|---|
|
The CanSpeak ability. This interface understands the command to speak , meaning that it has a corresponding method. |
|
Animals that have this feature.
To facilitate understanding, we provided the classes names in English. This is allowed in Java, but it is highly undesirable.
|
|
And how do we give them the command? |
When the number of classes in your programs reaches the thousands, you will not be able to live without interfaces. Instead of describing the interaction of thousands of classes, it is enough to describe the interaction of a few dozen interfaces — this greatly simplifies life.
And when combined with polymorphism, this approach is generally a smashing success.
3. The default
implementation of interface methods
Abstract classes can have variables and implementations of methods, but they cannot have multiple inheritance. Interfaces cannot have variables or implementations of methods, but that can have multiple inheritance.
The situation is expressed in the following table:
Ability/property | Abstract classes | Interfaces |
---|---|---|
Variables | ✔ | ✖ |
Method implementation | ✔ | ✖ |
Multiple inheritance | ✖ | ✔ |
So, some programmers really wanted interfaces to have the ability to have method implementations. But having the ability to add a method implementation does not mean that one will always be added. Add it if you want. Or if you don't, then don't.
In addition, problems with multiple inheritance are primarily due to variables. In any event, that's what they decided and did. Starting with JDK 8, Java introduced the ability to add method implementations to interfaces.
Here's an updated table (for JDK 8 and above):
Ability/property | Abstract classes | Interfaces |
---|---|---|
Variables | ✔ | ✖ |
Method implementation | ✔ | ✔ |
Multiple inheritance | ✖ | ✔ |
Now for abstract classes as well as interfaces, you can declare methods with or without an implementation. And this is excellent news!
In abstract classes, methods without an implementation must be preceded by the abstract
keyword. You don't need to add anything before methods with an implementation. In interfaces, the opposite is true. If a method does not have an implementation, then nothing should to be added. But if there is an implementation, then the default
keyword must be added.
For simplicity, we present this information in the following little table:
Ability/property | Abstract classes | Interfaces |
---|---|---|
Methods with no implementation | abstract |
– |
Methods with an implementation | – | default |
Problem
Using interfaces that have methods can greatly simplify large class hierarchies. For example, the abstract InputStream
and OutputStream
classes can be declared as interfaces! This lets us use them much more often and much more conveniently.
But there are already tens of millions (billions?) of Java classes in the world. And if you start changing standard libraries, then you might break something. Like everything! 😛
In order to not accidentally break existing programs and libraries, it was decided that method implementations in interfaces would have the lowest inheritance precedence.
For example, if one interface inherits another interface that has a method, and the first interface declares the same method but without an implementation, then the method implementation from the inherited interface will not reach the inheriting interface. Example:
interface Pet
{
default void meow()
{
System.out.println("Meow");
}
}
interface Cat extends Pet
{
void meow(); // Here we override the default implementation by omitting an implementation
}
class Tom implements Cat
{
}
The code will not compile because the Tom
class does not implement the meow()
method.
GO TO FULL VERSION