Generic Interface in Java

Introduction

A generic interface in Java allows you to define an interface with type parameters, making it more flexible and reusable. Generic interfaces enable you to define methods that can operate on various types while providing compile-time type safety. This guide will cover how to define and use generic interfaces in Java, along with examples to illustrate their usage.

Table of Contents

  1. What is a Generic Interface?
  2. Defining a Generic Interface
  3. Implementing a Generic Interface
  4. Example: Simple Generic Interface
  5. Bounded Type Parameters in Generic Interfaces
  6. Example: Generic Interface with Bounded Type Parameters
  7. Type Inference with Generic Interfaces
  8. Restrictions on Generic Interfaces
  9. Conclusion

1. What is a Generic Interface?

A generic interface is an interface that declares one or more type parameters. These type parameters can be used in the interface's methods, allowing the interface to work with different data types while maintaining type safety.

2. Defining a Generic Interface

A generic interface is defined by placing the type parameter(s) inside angle brackets (<>) after the interface name.

Syntax:

interface InterfaceName<T> {
    // Interface methods
}

3. Implementing a Generic Interface

When a class implements a generic interface, it must either specify the type parameters or remain generic itself.

Implementing a Generic Interface with Specific Type

Example:

interface GenericInterface<T> {
    void display(T value);
}

class StringPrinter implements GenericInterface<String> {
    @Override
    public void display(String value) {
        System.out.println("String value: " + value);
    }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        StringPrinter printer = new StringPrinter();
        printer.display("Hello, Generics!");
    }
}

Output:

String value: Hello, Generics!

Implementing a Generic Interface with Generic Type

Example:

interface GenericInterface<T> {
    void display(T value);
}

class GenericPrinter<T> implements GenericInterface<T> {
    @Override
    public void display(T value) {
        System.out.println("Value: " + value);
    }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        GenericPrinter<String> stringPrinter = new GenericPrinter<>();
        stringPrinter.display("Hello, Generics!");

        GenericPrinter<Integer> intPrinter = new GenericPrinter<>();
        intPrinter.display(123);
    }
}

Output:

Value: Hello, Generics!
Value: 123

4. Example: Simple Generic Interface

Example:

interface Pair<K, V> {
    K getKey();
    V getValue();
}

class OrderedPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;

    public OrderedPair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public K getKey() {
        return key;
    }

    @Override
    public V getValue() {
        return value;
    }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        Pair<String, Integer> pair = new OrderedPair<>("One", 1);
        System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());

        Pair<Integer, String> anotherPair = new OrderedPair<>(2, "Two");
        System.out.println("Key: " + anotherPair.getKey() + ", Value: " + anotherPair.getValue());
    }
}

Output:

Key: One, Value: 1
Key: 2, Value: Two

5. Bounded Type Parameters in Generic Interfaces

You can restrict the types that can be used as type arguments by using bounded type parameters. Bounded type parameters allow you to specify that a type must be a subclass (or implementor) of a specific class (or interface).

Syntax:

interface InterfaceName<T extends SuperClass> {
    // Interface methods
}

6. Example: Generic Interface with Bounded Type Parameters

Example:

interface Numeric<T extends Number> {
    double getDoubleValue(T value);
}

class NumericImpl<T extends Number> implements Numeric<T> {
    @Override
    public double getDoubleValue(T value) {
        return value.doubleValue();
    }
}

public class BoundedGenericInterfaceExample {
    public static void main(String[] args) {
        Numeric<Integer> intNumeric = new NumericImpl<>();
        System.out.println("Double value of Integer: " + intNumeric.getDoubleValue(100));

        Numeric<Double> doubleNumeric = new NumericImpl<>();
        System.out.println("Double value of Double: " + doubleNumeric.getDoubleValue(123.45));
    }
}

Output:

Double value of Integer: 100.0
Double value of Double: 123.45

7. Type Inference with Generic Interfaces

Java's compiler can infer the type parameters of a generic interface from the context in which it is used. This feature is known as type inference.

Example:

interface Displayable<T> {
    void display(T value);
}

class DisplayImpl<T> implements Displayable<T> {
    @Override
    public void display(T value) {
        System.out.println("Value: " + value);
    }
}

public class TypeInferenceExample {
    public static void main(String[] args) {
        Displayable<String> stringDisplay = new DisplayImpl<>();
        stringDisplay.display("Hello, Generics!");

        Displayable<Integer> intDisplay = new DisplayImpl<>();
        intDisplay.display(123);
    }
}

Output:

Value: Hello, Generics!
Value: 123

8. Restrictions on Generic Interfaces

There are several restrictions on generic interfaces in Java:

  1. Cannot Instantiate Generic Types with Primitive Types:

    // This is not allowed
    GenericInterface<int> intObj = new GenericInterface<>();
    
  2. Cannot Create Instances of Type Parameters:

    class GenericClass<T> {
        // This is not allowed
        // T obj = new T();
    }
    
  3. Cannot Declare Static Fields Whose Types are Type Parameters:

    class GenericClass<T> {
        // This is not allowed
        // static T obj;
    }
    
  4. Cannot Use Casts or instanceof with Parameterized Types:

    class GenericClass<T> {
        // This is not allowed
        // if (obj instanceof T) { }
        // T[] array = (T[]) new Object[10];
    }
    
  5. Cannot Create Arrays of Parameterized Types:

    // This is not allowed
    GenericInterface<String>[] stringArray = new GenericInterface<String>[10];
    

9. Conclusion

Generic interfaces in Java provide a powerful mechanism to define flexible, reusable, and type-safe interfaces. By using type parameters, you can create interfaces that can operate on various types, ensuring type safety and reducing the need for type casting. Bounded type parameters further enhance the flexibility of generic interfaces by allowing you to specify constraints on the types that can be used as arguments.

By understanding and utilizing generic interfaces, you can create more robust and maintainable Java applications.

Happy coding!

Comments