Automation QA Testing Course Content

Showing posts with label Interface. Show all posts
Showing posts with label Interface. Show all posts

Default methods in interfaces

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 enums. Default methods in interfaces - 1Similarly, 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! :)

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.