Interfaces
Many concepts in Java are derived from real-world scenarios. Interfaces as well. If you want to turn on a television, you need to press the power button. This button is the interface between you and the electronics inside the television. Java interfaces have the same behaviour as real-world interfaces (e.g., a television interface): they set strict rules (a blueprint, if you will) on how to interact with objects. As you have already learned, these strict rules can be defined with methods. In fact, Java objects define their interaction with the outside world through the methods that they expose.
In a nutshell, an Interface in Java is an abstract type used to specify how the outside world can interact with the objects that implement the interface.
How to create an Interface?
You create them as you create classes, but you must use the keyword interface instead of class.
Going back to our example with the television, let’s create an Interface called Television
with two methods pressOnButton()
and pressOffButton()
public interface Television {
public void pressOnButton();
public void pressOffButton();
}
The methods pressOnButton();
pressOffButton();
have no body (i.e., no {}
)—they just end with ;
—they are by default abstract
. In a nutshell, a Java interface represents a group of (abstract) methods with no bodies.
How to implement an Interface?
Now, we will create a class that implements our interface. Yes, a class does not extends an interface (like we did with classes), it implements it. For example, let’s create a class LCDTelevision
.
public class LCDTelevision implements Television {
@Override
public void pressOnButton() {
System.out.println("The television is on");
}
@Override
public void pressOffButton() {
System.out.println("The television is off");
}
}
We use the implements
keyword in the class declaration. The class gives a compilation error if we do not override the methods declared in the interface Television
. The interface Television
gives the methods that all the classes that implement a Television
must have. In our example, the pressOnButton()
and pressOffButton()
methods.
Now, if we want to instantiate an object LCDTelevision
we can do:
public static void main(String[] args) {
Television lcdTv = new LCDTelevision();
lcdTv.pressOnButton();
System.out.println("I am watching TV....");
lcdTv.pressOffButton();
}
Note that, interfaces like abstract classes cannot be instantiated (we cannot do Television tv = new Television();
) we must specify the implementation we want: Television lcdTv = new LCDTelevision()
).
A note on code style
Please note that the declared type of lcdTv
is Television
not LCDTelevision
. We could have also used LCDTelevision
as the type of lcdTv
(i.e., LCDTelevision lcdTv = new LCDTelevision();
). This would have worked fine.
However, depending on the situation, often it might be handy to store the reference of an object that implements an interface I
with a variable of type I
. This often leads to more maintainable code.
For example, it is considered good style to store a reference to a JDK ArrayList
or LinkedList
in a variable of type List
. List
is a common interface of both ArrayList
and LinkedList
.
public static void main(String[] args) {
List<String> names = new ArrayList<String>();
createNames(names);
}
public static void createNames(List<String> names){
// some code
}
In this way, if later you decide to use LinkedList
instead of an ArrayList
you just need to change one line of code: List<String> names = new ArrayList<String>();
to List<String> names = new LinkedList<String>();
. You do not need to change other lines of code that use the list names
. For example, you do not need to change the method declaration of method createNames
. Conversely, you must do it if the object’s reference was of type ArrayList
.
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<String>();
createNames(names);
}
public static void createNames(ArrayList<String> names){
// some code
}
Note that, in both cases: List<String> names = new ArrayList<String>();
and ArrayList<String> names = new ArrayList<String>();
, the names
reference variable points to an object of type ArrayList
. It is only the type of the reference that is in one case List
and in the other ArrayList
.
What is the difference between an abstract class and an interface?
I believe that right now, you are wondering what is the difference between an abstract class and an interface. You can think about interfaces as being “a really really abstract abstract class
. So abstract that it’s no longer a class – it’s now just an interface
“. An interface does not have any implementation in it. The table below summarizes the key differences between interfaces and abstract classes.
Property | Interface | Abstract class |
---|---|---|
Multiple Inheritance | A class can implement several Interfaces | A class can extend only one abstract class |
Contains | Abstract methods | Abstract methods and/or concrete methods |
When to use | It is better to use interfaces when various implementations share only method signatures. | It should be used when various implementations of the same kind share a common behavior. |
Implementation | An interface is abstract so that it cannot provide any implemented functionality. | An abstract class can give implementation that can be overridden. |
Inheritance | An interface can inherit multiple interfaces but cannot inherit a class. | An abstract class can inherit a class and multiple interfaces. |
Constructor | An interface cannot declare constructors | An abstract class can declare constructors |
Abstract keyword | In an interface, the abstract keyword is optional for declaring a method as abstract. | In an abstract class, the abstract keyword is compulsory for declaring a method as abstract. |
Class type | An interface can have only public abstract methods. | An abstract class can have protected abstract methods and public abstract methods. |
One of the key differences between interfaces and abstract classes is that you can implement more than one interface in one class (you cannot do this with abstract classes). You just need to separate the interface names with commas in the class declaration. For example, let’s say that we have an interface ElectricalAppliance
defined as:
public interface ElectricalAppliance {
public void plugIn();
public void plugOut();
}
Then, our LCDTelevision
can implement both interfaces: ElectricalAppliance
and Television
. Note that in this case, the LCDTelevision class has to provide the implementation of all four methods: pressOnButton
, pressOffButton
, plugIn
, and plugOff
.
public class LCDTelevision implements Television, ElectricalAppliance {
@Override
public void pressOnButton() {
System.out.println("The television is on");
}
@Override
public void pressOffButton() {
System.out.println("The television is off");
}
@Override
public void plugIn() {
System.out.println("The television is plug into an electricity supply");
}
@Override
public void plugOff() {
System.out.println("The television is plug off an electricity supply");
}
}
Why does Java need interfaces to support “multiple inheritance”? Why can’t a class extend multiple classes?
Java does not support “multiple inheritance” (a class can only inherit from one superclass). However, “multiple inheritance” can be achieved with interfaces—a class can implement multiple interfaces.
You might wonder why multiple inheritance is possible with interfaces but not with classes? The answer is because of ambiguity. Two classes might have different implementations of the same method or might have two different functionalities with the same method name.
For example, let’s assume that Java allows multiple inheritance with classes.
class A {
public void run() {
System.out.println("A");
}
}
class B {
public void run() {
System.out.println("B");
}
}
public class MyClass extends A, B { // ATTENTION extending two classes is not allowed
// we do it just for making a point :)
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.run(); // Which method should I execute?
}
}
Should the program print “A” or “B”? The JVM would not be able to decide which method to execute. There is ambiguity. However, there is no ambiguity if we use interfaces. It is because the implementation of the methods is never specified in interfaces. Interfaces specify only what the class is doing, not how it is doing it. How it is doing it must be specified by the class that implements the interface. For example, if we use interfaces, even if two interfaces have the same method names, there is no conflict or ambiguity. This is because it is MyClass
that defines the implementation.
interface A {
public void run();
}
interface B {
public void run();
}
public class MyClass implements A, B {
@Override
public void run() {
System.out.println("A");
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.run();
}
}
Can I have an “empty” interface that does not declare any methods?
Yes, you can! Is it something useful? Yes, indeed. For example, let’s say that we want to create a List
that contains Vehicles. Vehicles can be very different so I don’t expect that they can share functionalities. So we can use interfaces to just tell the JVM that the List can only contain Vehicle
objects.
import java.util.ArrayList;
import java.util.List;
interface Vehicle {
// empty interface
}
class Car implements Vehicle {
public void drive(int speed) {
// some implementation
}
}
class Airplane implements Vehicle {
public void fly(int speed, int altitude) {
// some implementation
}
}
class Boat implements Vehicle {
public void sail(int direction, int speed) {
// some implementation
}
}
public class MyClass {
public static void main(String[] args) {
List<Vehicle> vehicles = new ArrayList<Vehicle>();
vehicles.add(new Car());
vehicles.add(new Airplane());
vehicles.add(new Boat());
}
}
Can an interface inherit from another interface?
Yes, but instead of using implements you will use extends. In fact: a class extends another class, an interface extends another interface, but a class implements an interface.
For example you can do:
interface A {
public void funcA();
}
interface B extends A {
public void funcB();
}
class C implements B {
@Override
public void funcA() {
System.out.println("Implementation function A");
}
@Override
public void funcB() {
System.out.println("Implementation function B");
}
}
In this case, the class C
must implement both method funcA
and funcB
.
(Summary) Key Facts about Interfaces:
- Interface is a fundamental concept in Java OOP. It is used to achieve abstraction and multiple inheritance.
- Java Interface also represents the IS-A relationship.
- Like abstract classes, interfaces cannot be used to create objects (in the example above, it is not possible to instantiate a “Television” object).
- Interface methods do not have a body - the body is provided by the “implement” class
- An interface cannot contain a constructor (as it cannot be used to create objects)
- If a class implements an interface, it must implement all of the interface’s methods
- Interface methods are by default abstract and public
- Interface attributes are by default public, static and final
Get your hands dirty!
You are hiking in the forest, and you come across interesting items that you are allowed to gather and put in your backpack. Some items are eatable, some are not.
import java.util.ArrayList;
import java.util.List;
public class Mushroom {
private boolean isPoisonous() {
System.out.println("check online if it is poisonous...");
return false; // let's assume it is not
}
public void eat() {
if (!isPoisonous()) {
System.out.println("eating the mushroom");
}
}
}
public class Apple {
private void peal() {
System.out.println("pealing the apple");
}
public void eat() {
peal();
System.out.println("eating the apple");
}
}
public class Strawberry {
private void wash() {
System.out.println("wash the strawberry");
}
public void eat() {
wash();
System.out.println("eating the strawberry");
}
}
public class Stone {
public void examine() {
System.out.println("examining the stone");
}
}
public class WoodenStick {
public void use() {
System.out.println("use wooden stick to walk");
}
}
After a while, you feel hungry, and you want to eat all the eatable objects that you have found.
public class Main {
public static void main(String[] args) {
List<Object> backpack = new ArrayList<Object>();
System.out.println("Walking in the forest...");
System.out.println("I found a beautiful stone...");
backpack.add(new Stone());
System.out.println("I found a bush with 10 strawberries");
for (int i = 0; i < 10; i++) {
backpack.add(new Strawberry());
}
System.out.println("I found a wooden stick");
backpack.add(new WoodenStick());
System.out.println("I found 3 mushrooms and 1 apple");
backpack.add(new Mushroom());
backpack.add(new Mushroom());
backpack.add(new Mushroom());
backpack.add(new Apple());
System.out.println("another beautiful stone...");
backpack.add(new Stone());
System.out.println("I am tired and hungry");
System.out.println();
System.out.println(" I want to eat all the eatable items that I found");
for (Object item : backpack) {
if (item instanceof Strawberry) {
((Strawberry) item).eat();
} else if (item instanceof Apple) {
((Apple) item).eat();
} else if (item instanceof Mushroom) {
((Mushroom) item).eat();
}
}
}
}
The problem with this implementation is that if later on, we find new types of eatable objects (blueberries, walnuts, … ) we need to add other else if
conditions that would make the code verbose and difficult to maintain and modify. Let’s use interfaces to improve the design of our program!
Edit the Repl, which contains the code above, as follows:
- Create one interface
FoundObject
, all the classes (regardless if they represent eatable objects or not) should implement this interface. Should this interface have methods? Do all the classes have the same functionalities? - Change the type of the list
backpack
accordingly - Create another interface specif for eatable objects:
Eatable
. Classes that represent eatable objects should also implement this interface. Is there are any functionality common to all Eatable object? - Change the body of the
for loop
. Still, we should eat only eatable objects. (HINT: you should useinstance of
only once). - BONUS Can you modify the program so that we still have two interfaces but
Eatable
extendsFoundObject
? In this case, does the classApple
need to implement bothEatable
andFoundObject
interfaces? - BONUS Can you modify the program so that
FoundObject
has now aweight
field?, should you still use interfaces? or now is it better to use abstract classes?