Interface in Java Part 2

default methodscallback

In the previous blog we have learned about what Interface is, how it is implemented, etc. If you want to learn the Interface then you can visit Interface in Java . In this blog we will go through the remaining concept of Interface.

Default Methods

Java 8 introduced a new concept called default methods in the interface. Methods that are defined inside the interface and have a default keyword in the definition are called default methods. Default methods were introduced to solve the compatibility issues in Java.

Syntax

public interface interfaceName{
  …………
  default void defaultMethodName() {
    …………
  }
}

e.g.

public interface VehicleEx {
  default void print() {
    System.out.println("I am a Vehicle");
  }
}

public class Car implements VehicleEx{
  public static void main(String[] args){
    Car carName = new Car();
    carName.print();
  }
}

Output

I am a Vehicle

In the above code VehicleEx interface defines a method print() and provides a default implementation. Suppose any class implements this interface, then there is no need to implement its own version of the print() method. It will directly call object.print(). The other way is if we want to create our own custom method, then we can override the default method as follows:

e.g.

public class Car implements VehicleEx{
  public void print(){
    System.out.println("I am a fourWheeler Vehicle");
  }
  public static void main(String[] args){
    Car carName = new Car();
    carName.print();
  }
}

Output

AtrowelEmployeeSortEx.java

I am a fourWheeler Vehicle

The most important use of the default method is interface evolution. Suppose we provided class to the Collection:

e.g.

public class Car implements Collection

Later the steam method was added in an interface in Java 8. If the stream method was not a default method, then it would not be possible for the Car class to compile because it doesn’t implement the new method. Adding a non default method to an interface is not source compatible. Suppose we don’t recompile the class and simply use the old JAR file then the class will still load even with the missing method. Programs can construct Car objects and nothing will happen. If the program class calls the steam method on the Car object then an AbstractMethodError will occur. Using a default method solves both problems. The Car class will again compile. If the class is loaded without being recompiled and the stream method on a Bag object is executed, the Collection.stream method is invoked.


Resolving Default Method Conflicts

Suppose there is a situation where the same method is defined as a default method in one interface and again as a method of a superclass or another interface. Resolving this ambiguity in some languages like C++, Scala follows complex rules. But fortunately, Java provides much simpler rules:

  1. Default methods with the same name and parameter types are simply ignored if a superclass provides a concrete method. Superclass wins in this scenario.
  2. If an interface provides a default method, and another interface contains a method with the same name and parameter types, then we must resolve the conflict by overriding that method. In this case interface clashes. We will go through the second rule:
  3. e.g.

    interface Vehicle{
      default String printName() {
        return "Ford";
      };
    }
    interface Car{
      default String printName() {
        return "Maruti";
      }
    }

    If we define a class that implements both the interfaces.

    e.g.

    class FourWheeler implements Vehicle, Car{
        ............
    }

    In the above code the class FourWheeler inherits two printName() method from the Vehicle class and from the Car class. Rather than selecting one method from both, the Java compiler gives an error and leaves it to the programming to resolve ambiguity. Suppose printName() method is used in the FourWheeler class, then we can select one of the two conflicting methods as follows:

    e.g.

    class FourWheeler implements Vehicle, Car{
      public String printName() {
        return Vehicle.super.printName();
      }
      . . .
    }

    Suppose if we assume that the Car interface does not provide default implementation for printName():

    e.g.

    interface Car{
        String printName();
    }

    Now there may be a question raising in your mind that Can the FourWheeler class inherit the default method from Vehicle interface? This may be possible, but the Java designers wanted uniformity. It does not at all matter how the two interfaces conflict. If at least one interface provides an implementation, then the compiler gives an error and programmers have to resolve the ambiguity. As we have just discussed about the name clashes, now consider a class which extends a superclass and implements an interface, inheriting the same methods for both (superclass and interface). For example suppose a Vehicle is a class and FourWheeler is defined as follows:

    e.g.

    class FourWheeler extends Vehicle implements Car {
      . . .
    }

    In this case only the superclass will matter and if the default method is defined in the interface then it will be ignored. In the above example mentioned, FourWheeler inherits the printName() method from Vehicle, and it doesn’t make any difference whether the Car interface provides a default for printName() method or not. This is called the “class wins” rule. The “class wins” rule ensures compatibility with Java 7. Suppose even if we add default methods to an interface, it will have no effect on code which worked before. We must keep in mind that we can never make a default method which redefines the methods of Object class. For example we cannot define a default method for toString and equals.


Callback using Interfaces in Java

Callback refers to the technique of calling a function from another function in computer languages such as C and C++. We achieve callback by passing the function pointer(function's memory address) to another function. For example we may want a particular action to take place when the button is clicked or a menu item is selected. We will consider a simple situation. The package javax.swing contains a class called Timer class which is useful if we want to notify whenever a time interval has elapsed. There may be a question raised in the mind:How can we tell the timer what it should do ? The answer is in many languages we provide the name of a function that the time should call. The class in the Java standard library follows the object oriented approach i.e. we can pass an object to a call, then the timer calls one of the method on the object. It is very flexible to pass an object rather than passing a function on the object because the object contains more additional information. The timer must know what method it should call.

e.g.

public interface ActionListener{
    void actionPerformed(ActionEvent actionEventObj);
}

The timer will call the actionPerformed() which is defined in the above code. Suppose if we want to print a message, then we must define a class that implements ActionListener interface. We can then add the statement which we want to execute inside the actionPerformed().

e.g.

class TimeDisplay implements ActionListener{
  public void actionPerformed(ActionEvent actionEventObj){
    System.out.println("The time is: "+ Instant.ofEpochMilli(actionEventObj.getWhen()));
    Toolkit.getDefaultToolkit().beep();
  }
}

The object of ActionEvent actionEventObj provides the information of the event i.e. the time when the event occurred. The event.getWhen() method returns the time that is measured in millisecond. Next thing to do is we must construct an object of a class and must pass it to the Timer constructor.

e.g.

var eventListener = new TimeDisplay();
Timer timeObj = new Timer(1000, eventListener);

In the above code the first parameter of the Timer constructor is the time interval that must elapsed between notifications measured in milliseconds. The second parameter is the object of the Listener.

e.g.

import java.awt.*;
import java.awt.event.*;
import java.time.*;
import javax.swing.*;
public class TimerEx{
  public static void main(String[] args){
    var eventListener = new TimeDisplay();
    var timerObj = new Timer(1000, eventListener);
    timerObj.start();
    JOptionPane.showMessageDialog(null, "Quit program?");
    System.exit(0);
  }
}
class TimeDisplay implements ActionListener{
  public void actionPerformed(ActionEvent actionEventObj){
    System.out.println("The time is: "+ Instant.ofEpochMilli(actionEventObj.getWhen()));
    Toolkit.getDefaultToolkit().beep();
  }
}

Output

The time is: 2022-06-14T02:03:30.776Z
The time is: 2022-06-14T02:03:31.776Z
The time is: 2022-06-14T02:03:32.776Z
The time is: 2022-06-14T02:03:33.776Z
The time is: 2022-06-14T02:03:34.777Z
The time is: 2022-06-14T02:03:35.778Z
The time is: 2022-06-14T02:03:36.780Z
The time is: 2022-06-14T02:03:37.779Z
The time is: 2022-06-14T02:03:38.780Z
The time is: 2022-06-14T02:03:39.780Z
The time is: 2022-06-14T02:03:40.780Z
The time is: 2022-06-14T02:03:41.780Z
The time is: 2022-06-14T02:03:42.781Z
The time is: 2022-06-14T02:03:43.781Z
The time is: 2022-06-14T02:03:44.782Z
The time is: 2022-06-14T02:03:45.782Z
The time is: 2022-06-14T02:03:46.782Z
The time is: 2022-06-14T02:03:47.783Z
The time is: 2022-06-14T02:03:48.783Z
The time is: 2022-06-14T02:03:49.784Z
The time is: 2022-06-14T02:03:50.784Z
The time is: 2022-06-14T02:03:51.784Z
The time is: 2022-06-14T02:03:52.784Z
The time is: 2022-06-14T02:03:53.785Z
The time is: 2022-06-14T02:03:54.785Z
The time is: 2022-06-14T02:03:55.786Z
The time is: 2022-06-14T02:03:56.786Z
The time is: 2022-06-14T02:03:57.786Z
The time is: 2022-06-14T02:03:58.787Z
The time is: 2022-06-14T02:03:59.787Z
The time is: 2022-06-14T02:04:00.788Z
The time is: 2022-06-14T02:04:01.788Z
Get in Touch

Atrowel will be pleased to receive your feedback and suggestions. Those of you who would like to contribute, please send us an email.