This is a stripped down text version of the video Java Interfaces Explained. The video has many more examples and in depth explanations.

What is an Interface?

An interface in Java is a contract or blueprint for a class. It specifies what a class must do, but not how it does it.

For instance, consider this interface Playable, which includes a method play. This method has no body.

public interface Playable {
    void play();
}

The naming convention for interfaces often includes suffixes like -able or -ible, such as Renderable, Comparable, or Playable. Sometimes, interfaces are named after a property they represent, like HasSize, or simply as nouns like List or Set.

We can have different implementations of this interface, like Video and Audio, each defining its own version of play:

public class Video implements Playable {
    @Override
    public void play() {
        // play the video
    }
}

public class Audio implements Playable {
    @Override
    public void play() {
        // play the audio
    }
}

Why Use Interfaces?

Interfaces allow for writing implementation-agnostic code, meaning the code doesn’t rely on the specific details of how each class accomplishes its tasks.

This approach offers numerous benefits:

  1. Reduced Coupling: By using interfaces, classes become less dependent on each other’s specifics, enhancing flexibility and modularity in the code.
  2. Code Reusability: Interfaces enable multiple classes to implement the same set of behaviors in different ways, promoting code reuse.
  3. Enhanced Scalability: They allow systems to scale and evolve over time, accommodating new functionalities without disrupting existing implementations.

Imagine a Player class that can play any Playable object. This design allows us to pass any object that implements Playable, such as Video or Audio, to the Player class without it needing to know the specifics of these classes.

public class Player {
    void play(Playable playable) {
        playable.play();
    }
}

Java 8 Interface Features

With Java 8, interfaces gained additional capabilities, moving closer to abstract classes.

Let’s explore some of these advanced features:

  1. Default Methods: Interfaces can now include methods with a default implementation, allowing shared functionality among implementing classes.
  2. Static Methods: Like static methods in classes, these are attached to the interface and can be accessed without an instance.
  3. Functional Interfaces: These special interfaces with a single abstract method can be implemented using lambda expressions or method references.

Default Methods

With Java 8, interfaces were enhanced with the ability to have default methods. These are methods within an interface that have a default implementation. This feature allows for greater flexibility and the ability to add new functionality to interfaces without breaking existing implementations.

public interface Logger {
    void log(String message);

    default void logError(String message) {
        log("ERROR: " + message);
    }
}

In this example, Logger interface has a default method logError that utilizes the log method. Implementing classes can override this default method if a specific behavior is required.

Static Methods

Java 8 also introduced the possibility of defining static methods in interfaces. These methods are attached to the interface itself and not to the object instances, similar to static methods in classes.

public interface UserStore {
    void putUser(User user);
    User getUser(String username);
    
    static String getDefaultUsername() {
        return System.getProperty("user.name");
    }
}

Here, UserStore interface contains a static method getDefaultUsername. This method can be accessed without creating an instance of the interface, using UserStore.getDefaultUsername().

Functional Interfaces

A significant feature introduced in Java 8 is the concept of functional interfaces. A functional interface is an interface with exactly one abstract method, and it can be used alongside lambda expressions and method references.

@FunctionalInterface
interface Processor {
    void process(String data);
}

This Processor interface is a functional interface. It can be implemented using a lambda expression:

Processor processor = data -> System.out.println(data);

Or a method reference:

Processor processor = System.out::println;

Functional interfaces are a key part of Java’s lambda expressions, allowing for concise and expressive code.

Interface Inheritance

Java interfaces support the concept of inheritance, similar to classes. One interface can extend another, inheriting its methods. This allows for a more modular and structured approach to interface design.

Extending interfaces allows for adding new functionality to an existing interface without altering the original interface’s contract. This is particularly useful for maintaining backward compatibility.

public interface Logger {
    void log(String message);
}

public interface ErrorLogger extends Logger {
    void logError(String errorMessage);
}

ErrorLogger extends Logger, adding a new method logError, while still maintaining the original log method from Logger.

Generic Interfaces

Interfaces in Java can be generic, allowing for type parameters. This makes interfaces more flexible and reusable, as they can be used with various data types.

public interface Store<T> {
    void put(T item);
    T get(int id);
}

The Store interface is a generic interface with a type parameter T. This allows for the creation of repositories for different data types, like Store<Integer> or Store<User>.

I have a full video explaining generics in detail: Java Generics Explained

Watch the video for more in-depth examples and explanations

To dive deeper into Java interfaces and see these concepts in action with in-depth examples and explanations, I highly recommend watching the video Java Interfaces Explained. This video provides a more complete understanding and is an excellent resource for anyone looking to master Java interfaces.

Watch the video here: Java Interfaces Explained