The word “interface” means a medium or means of communication between two entities. Similarly, in Java, interfaces are used to ease the communication between various classes.
In Java, an interface looks similar to any class and contains only constants, method signatures, default methods, and static methods. Before going into the implementation and uses of interfaces, first, let us understand why we need them.
Many times, separate groups of developers have to agree to a basic reference about how their software interacts. Ideally, each group should be able to write their code without knowing the other group’s code. This is where interfaces come into play in defining such contracts.
For example, assume that cars are driven by software (quite true these days). Now, the automobile manufacturers write software to operate any car consisting of operations like — stop, turn left, turn right, start, etc. Now, you know Machine learning, by which you have written algorithms for a self-driving car, but to make the car drive, you must know how to make the car stop, turn in either direction, start, etc.
So there is a need for a common interface between the two groups (manufacturers and driving unit). The manufacturers must provide this interface that spells out what methods can be invoked to operate the car. The driving unit then writes software using the interface methods according to their needs. This way, neither group needs to know how another group is writing its code.
The basic needs that an interface fulfills are complete abstraction by using abstract methods; it also helps in achieving multiple inheritance in Java (by extending interfaces) and defining a prototype of properties of a family of classes which ensures loose coupling.
Now let’s study interfaces technically.
An interface declaration consists of an access modifier, interface keyword, name of the interface, list of parent interfaces (comma separated if any exists), and the body.
Syntax:
public interface NameInterface extends Interface1, Interface2 {
//constants
//method signatures
//rest body...
}
Note: The part in bold involves inheritance; you can refer to the basics of inheritance here.
The body can contain abstract, default, static methods, and constants. An abstract method does not have a definition, default and static methods are declared with default and static keywords respectively, and both have definitions. All methods in an interface are implicitly public. All constants in an interface are implicitly public, static, and final.
The interface written by the manufacturing unit of our self-driving car may look like this:
public interface Controls {
//constants
int maxSpeed = 210;
//abstract methods
int turnLeft(int distance);
int turnRight(int distance);
int accelerate (int distance, int toSpeed);
int brake(int distance, int toSpeed);
//default methods
default void stop(int distance){
System.out.println("STOP!");
}
//rest part of the interface
}
Now that we have the interface given by the manufacturing team, the driving team will implement it as per their need. By implementing an interface, we mean to define classes with an implements clause followed by the name of interfaces they implementand provide suitable definitions to the abstract methods of the interface.
Syntax:
public class Driving implements Controls{
//methods and variables of the Driving class...
//giving definition to abstract methods of Controls
public int turnLeft(int distance){
//some code
}
public int turnRight(int distance){
//some code
}
//other definitions...
}
Note:
That’s it! Both the driving and manufacturing teams could write their code with great ease and without depending on each other, thanks to Java interfaces.
An interface is a reference type, which means we can use it as a reference data type just like any other class. But, a reference of interface type must be assigned with an instance of a class that implements that interface.
Consider this example :
public interface Animal {
void sound();
}
public class Dog implements Animal{
public void name(){
System.out.println("JIMMY");
}
//implementing method sound()
public void sound(){
System.out.println("BARK");
}
}
public class Man{
//class body...
}
public class Test {
public static final void main(String[] args) {
Animal animal = new Dog();
animal = new Man();
}
}
The bold phase line generates an error because the Man class does not implement the Animal interface; hence the Animal reference type can not reference it. Also, think, a man is not an animal, right? Now, consider this main method:
public static final void main(String[] args) {
Animal animal = new Dog();
animal.sound();
animal.name();
}
The bold phase line is erroneous because the reference variable is of Animal type, and the Animal interface does not have the name() method. However, the sound method will print “BARK” of the instance of Dog class.
To use the name() method, we have to use the reference of Dog type, that is :
public static final void main(String[] args) {
Animal animal = new Dog();
animal.sound();
Dog dog = (Dog)(animal);
dog.name();
dog.sound();
}
//alternatively we can write ((Dog)animal).name();
//The bold phase lines involves typecasting.
Imagine that in the future, the manufacturing team of the self-driving car has to add some features to their interface. If they add those features to the existing interface, then all the classes that implement it will break, and the clients will be unhappy.
One alternative is that the manufacturing team creates a new interface that will extend the existing interface, and the users will have to upgrade to this new interface.
Alternatively, the features can be added as default methods to the interface because the default methods allow us to add new functionalities to the interface and ensure binary compatibility with code written for the existing interface.
Enjoy learning. Enjoy OOPS!
Subscribe to get well designed content on data structure and algorithms, machine learning, system design, object orientd programming and math.