Generic Class in Java

Introduction

A generic class in Java allows you to define a class with type parameters, making the class more flexible and reusable. Generics provide compile-time type safety, eliminating the need for type casting and reducing runtime errors.

Table of Contents

  1. What is a Generic Class?
  2. Defining a Generic Class
  3. Benefits of Using Generic Classes
  4. Creating Instances of a Generic Class
  5. Example: Simple Generic Class
  6. Bounded Type Parameters in Generic Classes
  7. Example: Generic Class with Bounded Type Parameters
  8. Type Erasure
  9. Restrictions on Generic Classes
  10. Conclusion

1. What is a Generic Class?

A generic class is a class that can operate on objects of various types while providing compile-time type safety. You define a generic class with one or more type parameters, which can be used throughout the class for various purposes.

2. Defining a Generic Class

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

Syntax:

class ClassName<T> {
    // Class body
}

3. Benefits of Using Generic Classes

  • Type Safety: Generic classes ensure that the type of objects passed to and returned by the class is consistent, reducing runtime errors.
  • Code Reusability: A single generic class can be used with different types, reducing code duplication.
  • Elimination of Type Casting: Explicit type casting is not needed when using generic classes.

4. Creating Instances of a Generic Class

To create an instance of a generic class, you need to specify the type argument in angle brackets.

Example:

GenericClass<Integer> intObj = new GenericClass<>();
GenericClass<String> strObj = new GenericClass<>();

5. Example: Simple Generic Class

Example:

// Defining a simple generic class
class GenericBox<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

public class GenericClassExample {
    public static void main(String[] args) {
        // Creating an instance of a generic class for Integer type
        GenericBox<Integer> intBox = new GenericBox<>();
        intBox.setValue(100);
        System.out.println("Integer Value: " + intBox.getValue());

        // Creating an instance of a generic class for String type
        GenericBox<String> strBox = new GenericBox<>();
        strBox.setValue("Java Generics");
        System.out.println("String Value: " + strBox.getValue());
    }
}

Output:

Integer Value: 100
String Value: Java Generics

Explanation:

  • The GenericBox class is defined with a type parameter T.
  • The setValue and getValue methods use the type parameter T.
  • Instances of GenericBox are created for Integer and String types.

6. Bounded Type Parameters in Generic Classes

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:

class ClassName<T extends SuperClass> {
    // Class body
}

7. Example: Generic Class with Bounded Type Parameters

Example:

// Defining a generic class with a bounded type parameter
class NumberBox<T extends Number> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public double getDoubleValue() {
        return value.doubleValue();
    }
}

public class BoundedGenericClassExample {
    public static void main(String[] args) {
        // Creating an instance of a generic class for Integer type
        NumberBox<Integer> intBox = new NumberBox<>();
        intBox.setValue(100);
        System.out.println("Integer Value: " + intBox.getValue());
        System.out.println("Double Value: " + intBox.getDoubleValue());

        // Creating an instance of a generic class for Double type
        NumberBox<Double> doubleBox = new NumberBox<>();
        doubleBox.setValue(123.45);
        System.out.println("Double Value: " + doubleBox.getValue());
        System.out.println("Double Value: " + doubleBox.getDoubleValue());
    }
}

Output:

Integer Value: 100
Double Value: 100.0
Double Value: 123.45
Double Value: 123.45

Explanation:

  • The NumberBox class is defined with a bounded type parameter T extends Number.
  • The setValue, getValue, and getDoubleValue methods use the type parameter T.
  • Instances of NumberBox are created for Integer and Double types.

8. Type Erasure

Type erasure is a process by which the compiler removes all information related to generic type parameters and replaces them with their bounds or Object if the type parameter is unbounded. This ensures compatibility with older versions of Java that do not support generics.

Example:

public class TypeErasureExample<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public static void main(String[] args) {
        TypeErasureExample<Integer> intBox = new TypeErasureExample<>();
        intBox.setValue(100);
        System.out.println("Integer Value: " + intBox.getValue());

        TypeErasureExample<String> strBox = new TypeErasureExample<>();
        strBox.setValue("Java Generics");
        System.out.println("String Value: " + strBox.getValue());
    }
}

Explanation:

  • During compilation, the generic type T is erased and replaced with Object or the upper bound specified.

9. Restrictions on Generic Classes

There are several restrictions on generic classes in Java:

  1. Cannot Instantiate Generic Types with Primitive Types:

    // This is not allowed
    GenericClass<int> intObj = new GenericClass<>();
    
  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
    GenericClass<String>[] stringArray = new GenericClass<String>[10];
    

10. Conclusion

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

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

Happy coding!

Comments