Computer scienceProgramming languagesJavaCode organizationObject-oriented programmingClass hierarchiesInterfaces and abstract classes

Fundamentals of interfaces in Java

6 minutes read

In the previous topic, we learned about interfaces' usage and how a class can implement an interface with methods from it. We barely mentioned access modifications and different method types. Now let's talk about their rules and the possibilities they uncover.

Multiple inheritance

But what if we want a class to inherit from multiple interfaces. The use case is simple: animals of class amphibians can live both in water and on land so all of them must have some characteristics from these kinds. This is called multiple inheritance.

interface Fish { }
interface Terrestrial { }
    
class Amphibian implements Fish, Terrestrial { }

Or if we know beforehand that we will need to create multiple classes from a class with multiple inheritance we can create a new interface that will extend one or more other interfaces using the keyword extends:

interface Fish { }
interface Terrestrial { }

interface Amphibian extends Fish, Terrestrial { }

In case you need to combine some properties a class can also extend another class and implement multiple interfaces like this:

class A { }

interface B { }
interface C { }
    
class D extends A implements B, C { }

Multiple inheritances of interfaces are also often used in the Java standard class library. The class String, for example, implements three interfaces at once:

public final class String 
    implements java.io.Serializable, Comparable<String>, CharSequence {
// ...
}

Now let's discuss method types. There are three: abstract, default, and static.

Abstract methods

So by default, if we do not provide a method with a body it will be abstract. Meaning it can only have a declaration, not a definition. They must be defined later in the implementing classes though. Here is a simple interface example with an abstract method breathe:

interface Fish {
    abstract void breathe();
}

You can omit the abstract keyword.

Default methods

Default methods are defined with the default modifier and mean that these methods can be accessible from classes implementing the interface:

interface Fish {
    default void breathe(){
        System.out.println("A breath was taken!");
    }
}

Static methods

In addition to default methods, you can declare and implement a static method in an interface via static keyword.
These methods you can use by invoking it directly from an interface:

interface Car {
    static double convertToMilesPerHour(double kmh) {
        return 0.62 * kmh;
    }
}

Car.convertToMilesPerHour(4.5);

The main purpose of static methods is to define utility functionality that is common for all classes implementing the interface. They help to avoid code duplication and create additional utility classes. For example the System.out.println is also a static method.

Marker interfaces

In some situations, an interface has no members at all. Such interfaces are called marker or tagged interfaces. For example, a well-known interface Serializable is a marker interface:

public interface Serializable { 
}

Other examples of marker interfaces are Cloneable, Remote, etc. They are used to provide essential information to the JVM.

Functional interfaces

In all the rest interfaces above we implied the methods implementation.

The interfaces with only one abstract method and any number of static, default, and public are called functional otherwise we will call interfaces with multiple abstract methods normal.

There is also an annotation @FunctionalInterface that ensures the interface has only one abstract method although using that annotation is not mandatory. The restriction needs to be respected for the sake of properly working lambda functions.

interface Fish {
    void breathe();
}

A typical functional interface example from the default library is ActionListener which only contains the actionPerformed() method.

Access modifiers

In the previous part, we briefly used access modifiers with interface methods. Let's discuss all possible modifiers a method can have in the interface.

In the latest Java versions, we can define:

  • public—a method that we can use from any class or package;

  • private—a method that we can access only from the same interface. We can also use it with static.

Note that all methods are public by default.

Now, let's summarize all the options for interface contents in this example:

An interface cannot contain constructors, non-public abstract methods, or any fields other than public static final (constants). Here's how we declare an interface containing all possible members:

interface Interface {
        
    int INT_CONSTANT = 0; // it's a constant, the same as public static final int INT_CONSTANT = 0
        
    void instanceMethod();  // An abstract method 
        
    static void staticMethod() {
        System.out.println("Interface: static method");
    }

    private static void staticPrivateMethod() {
        System.out.println("Interface: private static method");
    }
        
    default void defaultMethod() {
        System.out.println("Interface: default method. It can be overridden");
    }

    private void privateMethod() {
        System.out.println("Interface: private methods in interfaces are acceptable but should have a body");
    }
}

Conclusion

In this topic, we learned about ways for class to implement multiple interfaces and for interface to extend multiple interfaces. Got to know about all the types of methods in an interface and about their access modifications so we can predefined overriding strategies and restrict access for some methods.

5 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo