Hi! Today we're going to talk about an important concept in Java: interfaces.
The word is probably familiar to you. For example, most computer programs and games have interfaces. In a broad sense, an interface is a kind of 'remote control' that connects two interacting parties.
A simple example of an interface in everyday life is a TV remote control.
It connects two object — a person and a TV — and performs different tasks: turn up or turn down the volume, switch channels, and turn on or turn off the TV.
One party (the person) needs to access the interface (press a button on the remote control) to cause the second party to perform the action. For example, to make the TV change to the next channel. What's more, the user doesn't need to know how the TV is organized or how the channel changing process is implemented internally.
The only thing the user has access to is the interface. The main objective is to get the desired result.
What does this have to do with programming and Java? Everything :)
Creating an interface is very similar to creating a regular class, but instead using the word
We've created a
"What do we see here? The
Now we can simply create our messenger classes that implement the corresponding interface. The compiler itself will 'force' us to implement them in our classes. Telegram:
WhatsApp:
Viber:
What advantages does this provide? The most important of them is loose coupling. Imagine that we're designing a program that will collect client data. The
We created three fields, but a client can have only one messenger. We just don't know which one. So we have to add every possibility to the class in order to be able to communicate with the client. It turns out that one or two of them will always be
This is an example of loose coupling! Instead of specifying a specific messenger class in the
Of course, an app and a messenger are similar, but they're still different things. There can be mobile and desktop versions of a messenger, but App specifically represents a mobile app. Here's the deal — if we used inheritance, we wouldn't be able to add a
Now we're using the
Using the
class
, we indicate the word interface
.
Let's look at the simplest Java interface, see how it works, and why we would need it:
We've created a
CanSwim
interface. It's a bit like our remote control, but with one 'button': the swim()
method.
But how do we use this remote controller?
To do this, we need to implement a method, i.e. our remote control button. To use an interface, some classes in our program must implement its methods.
Let's invent a class whose objects 'can swim'. For example, a Duck
class fits:
"What do we see here? The
Duck
class is 'associated' with the CanSwim
interface by the implements
keyword. You may recall that we used a similar mechanism to associate two classes through inheritance, but in that case we used the word extends.
For complete clarity, we can translate 'public class Duck implements CanSwim
' literally as: 'The public Duck
class implements the CanSwim
interface'.
This means that a class associated with an interface must implement all of its methods. Note: our Duck
class, just like the CanSwim
interface, has a swim()
method, and it contains some logic.
This is a mandatory requirement. If we just write public class Duck implements CanSwim
without creating a swim()
method in the Duck
class, the compiler will give us an error:
Duck is not abstract and does not override abstract method swim() in CanSwim
Why? Why does this happen?
If we explain the error using the TV example, it would be like handing someone a TV remote control with a 'change channel' button that can't change channels.
You could press the button as much as you like, but it won't work. The remote control doesn't change channels by itself: it only sends a signal to the TV, which implements the complex process of channel changing.
And so it is with our duck: it must know how to swim so it can be called using the CanSwim
interface.
If it doesn't know how, the CanSwim
interface doesn't connect the two parties — the person and the program. The person won't be able to use the swim()
method to make a Duck
swim inside the program.
Now you understand more clearly what interfaces are for.
An interface describes the behavior that classes implementing the interface must have. 'Behavior' is a collection of methods.
If we want to create several messengers, the easiest thing to do is to creating a Messenger
interface. What does every messenger need? At a basic level, they must be able to receive and send messages.
Now we can simply create our messenger classes that implement the corresponding interface. The compiler itself will 'force' us to implement them in our classes. Telegram:
WhatsApp:
Viber:
What advantages does this provide? The most important of them is loose coupling. Imagine that we're designing a program that will collect client data. The
Client
class definitely needs a field to indicate which specific messenger the client is using.
Without interfaces, this would look weird:
We created three fields, but a client can have only one messenger. We just don't know which one. So we have to add every possibility to the class in order to be able to communicate with the client. It turns out that one or two of them will always be
null
, entirely unneeded by the program.
It's better to use our interface instead:
This is an example of loose coupling! Instead of specifying a specific messenger class in the
Client
class, we just indicate that the client has a messenger. Which one exactly will be determined while the program runs.
But why do we need interfaces for this? Why were they even added to the language?
That's a good question — and the right question! Can't we achieve the same result using ordinary inheritance?
The Messenger
class as the parent, and Viber
, Telegram
, and WhatsApp
as the children. Indeed, that is possible.
But there's one snag. As you already know, Java has no multiple inheritance. But there is support for multiple interfaces. A class can implement as many interfaces as you want.
Imagine that we have a Smartphone
class that has one App
field, which represents an app installed on the smartphone.
Of course, an app and a messenger are similar, but they're still different things. There can be mobile and desktop versions of a messenger, but App specifically represents a mobile app. Here's the deal — if we used inheritance, we wouldn't be able to add a
Telegram
object to the Smartphone
class. After all, the Telegram
class cannot simultaneously inherit App
and Messenger
! And we already made it inherit Messenger
and added it to the Client
class.
But the Telegram
class can easily implement both interfaces! Accordingly, we can give the Client
class a Telegram
object as a Messenger
, and we can give it to the Smartphone
class as an App
. Here's how you do that:
Now we're using the
Telegram
class how we want to. In some places, it acts as an App
. In other places, it acts as a Messenger
.
You've surely already noticed that interface methods are always 'empty', i.e. they have no implementation.
The reason for this is simple: the interface describes behavior, but it doesn't implement it. 'All objects that implement the CanSwim
interface must be able to swim': that's all that the interface tells us. The specific way that fish, ducks, and horses swim is a question for the Fish
, Duck
, and Horse
classes, not the interface. Just like changing the channel is a task for the TV. The remote simply gives you a button for this.
However, an interesting addition appeared in Java 8 — default methods.
For example, your interface has 10 methods. 9 of them have different implementations in different classes, but one is implemented the same for all. Previously, before Java 8, interface methods had no implementation whatsoever: the compiler immediately gave an error. Now you can do something like this:
Using the
default
keyword, we've created an interface method with a default implementation. We need to provide our own implementation for two other methods — eat()
and run()
— in all classes that implement CanSwim
. We don't need to do this with the swim()
method: the implementation will be the same in every class.
By the way, you've already come across interfaces in past tasks, even if you didn't notice :) Here's a vivid example:
You've worked with the List
and Set
interfaces! More precisely, you've worked with their implementations — ArrayList
, LinkedList
, HashSet
, etc.
The same diagram clearly gives an example where one class implements multiple interfaces at the same time. For example, LinkedList
implements the List
and Deque
(double-ended queue) interfaces.
You're familiar with the Map
interface, or rather, with its HashMap
implementation.
By the way, this diagram illustrates a feature: interfaces can be inherit other interfaces. The SortedMap
interface inherits Map
, while Deque
inherits Queue
. This is necessary if you want to show the relationship between interfaces, where one interface is an extended version of another.
Let's consider an example with the Queue
interface. We haven't yet reviewed Queues
, but it's rather simple and works like an ordinary queue, or line, at a store.
You can only add items to the end of the queue, and can only take them from the beginning. At some point, developers needed an enhanced version of the queue in order to add and take items at both ends. So they created a Deque
interface, which is a double-ended queue. It has all the methods of an ordinary queue. After all, it's the parent of the double-ended queue, but it also adds new methods.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.