Automation QA Testing Course Content

Why interfaces are necessary in Java

Hi! Today we're going to talk about an important concept in Java: interfaces. Why interfaces are necessary in Java - 1The 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 class, we indicate the word interface. Let's look at the simplest Java interface, see how it works, and why we would need it:
public interface CanSwim {

     public void swim();
}
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:
public class Duck implements CanSwim {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
"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.
public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
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:
public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Telegram message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Telegram message!");
     }
}
WhatsApp:
public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
Viber:
public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Viber message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Viber message!");
     }
}
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:
public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
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:
public class Client {

    private Messenger messenger;
}
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.
public class Smartphone {

    private App app;
}
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:
public class Telegram implements Application, Messenger {

    // ...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
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:
public interface CanSwim {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
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: Why interfaces are necessary in Java - 2You'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.