Interface in Java

In Java, an interface is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. (Since Java 8) Method bodies exist only for default methods and static methods. Interfaces cannot be instantiated, they can only be implemented by classes or extended by other interfaces.

Interface is used to achieve abstraction and multiple inheritance in Java.

Multiple Inheritance Problem


Java support multi-level inheritance

class GrandParent {  
    // GrandParent members
}

class Father extends GrandParent {  
    // Father members
}  

//Multi Inheritance  
class Child extends Father {  
    // Child members  
}
                    

but not multiple-inheritance

class GrandParent {  

    void print() { 
        System.out.println("Grandparent"); 
    } 
}

class Father extends GrandParent {  

    @Override
    void print() { 
        System.out.println("Father"); 
    }
}  

class Mother extends GrandParent {  

    @Override
    void print() { 
        System.out.println("Mother"); 
    }
}  

// Multi Inheritance  
class Child extends Father, Mother {  
    // Child members
}

class TestChild {  

    void test() {
        Child c = new Child();
        c.print();
    }
}

/*
compilation error: '{' expected
*/
                    

From the code, we see that: On calling the method print() using Child object will cause complications such as whether to call Father’s print() or Mother’s print() method. This problem is known as the diamond problem.

The Diamond Problem

The Diamond Problem

The Diamond Problem

The diamond problem (or sometimes referred too as the "deadly diamond of death") is the generally used term for an ambiguity that arises when two classes B and C inherit from a superclass A, and another class D inherits from both B and C. If there is a method "m" in A that B or C (or even both of them) has overridden, and furthermore, if does not override this method, then the question is which version of the method does D inherit? It could be the one from A, B or C .

In our case both Father (B) and Mother (C) are sub classes of GrandParent (A), and override print() (the m) method from GrandParent. When Child (D) inherits from Father (B) and Mother (C), which print() method (m) Child inherits?

Therefore, in order to avoid such complications Java does not support multiple inheritance of classes.

Why Interface?

Interfaces are great for putting together plug-n-play like architectures where components can be interchanged at will. Since all interchangeable components implement the same interface, they can be used without any extra programming. The interface forces each component to expose specific public members that will be used in a certain way.

Because interfaces must be implemented by derived classes, they define a contract.

Let's check our example:

Loggable.java
public interface Loggable {
    
    void log(String message);    
}
                    

Different classes can implement Loggable by providing an implementation of the log() method, for example, the ConsoleLog class logs the message on the console, FileLog logs the message into a log file, and DBLog class logs the message a record in the database.

ConsoleLog.java
public class ConsoleLog implements Loggable {

    @Override
    public void log(String message) {
        System.out.println(message);
    }    
}
                    

FileLog.java
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;

public class FileLog implements Loggable {

    @Override
    public void log(String message) {
        try {
            final Path path = Paths.get("path/to/filename.log");
            Files.write(path, Arrays.asList(message), StandardCharsets.UTF_8,
                    Files.exists(path) ? StandardOpenOption.APPEND : StandardOpenOption.CREATE);
        } catch (final IOException iox) {
            // Add exception handling here
        }
    }    
}
                    

DBLog.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
 
public class DBLog implements Loggable {
 
    private Connection connect() {
        String url = "jdbc:sqlite:log.db";
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url);
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
        return conn;
    }
 
    @Override
    public void log(String message) {
        String sql = "INSERT INTO tb_log(message) VALUES(?)";
 
        try (Connection conn = this.connect();
                PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, message);
            pstmt.executeUpdate();
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    }
}
                    

Now, you can instantiate an object. It's depends on the situation, you want to use ConsoleLog, FileLog, or DBLog class:

Loggable consolelog = new ConsoleLog();
consolelog.log("Log into console");
// or
Loggable filelog = new FileLog();
filelog.log("Log into a file");
// or
Loggable dblog = new DBLog();
dblog.log("Log into a database");
                    

Implement Multiple Interfaces

Conceptual Example

A cat can eat, so the Cat class may implement Eatable Interface.

A cat can make sound (such as meow, yowling, hissing, and growling... OK, we not talking about cat), so the Cat class may implement Soundable Interface.

Because an instance of the class could have different behaviors, we could have different corresponding interfaces.

Eatable.java
public interface Eatable {
    
    void eat();
}
                    

Soundable.java
public interface Soundable {
    
    void sound();
}
                    

Cat.java
public class Cat implements Eatable, Soundable {

    @Override
    public void eat() {
        System.out.println("A cat able to eat");
    }

    @Override
    public void sound() {
        System.out.println("A cat able to make sound");
    }
    
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
        cat.sound();
    }
}

/* 
Output:
------    
A cat able to eat
A cat able to make sound
*/
                    

Real-life Example

Class ArrayList (in java.util package), implements following interfaces:

  • List
  • RandomAccess
  • Cloneable
  • Serializable

extends AbstractList which extends AbstractCollection which implements:

  • Collection which extends from interface Iterable
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable