Java.Core.What are generics?

Generics in Java allow you to write type-safe, reusable, and flexible code by enabling parameterized types. They help avoid type casting and runtime ClassCastException errors.


1. Why Use Generics?

🚀 Before Generics (Java <5), You Had to Use Object

import java.util.*;

public class WithoutGenerics {
    public static void main(String[] args) {
        List list = new ArrayList(); // No type specified
        list.add("Java");
        list.add(10); // Allowed, but problematic

        String s = (String) list.get(0); // ✅ Works
        String num = (String) list.get(1); // ❌ ClassCastException at runtime
    }
}

🛑 Problem: Since List holds Object, manual casting is needed, and incorrect casting leads to runtime errors.


🚀 With Generics (Java 5+)

import java.util.*;

public class WithGenerics {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(); // Type-safe collection
        list.add("Java");
        // list.add(10); // ❌ Compilation error (not a String)

        String s = list.get(0); // ✅ No casting needed, type safety ensured
    }
}

Advantages of Generics:

  • Eliminates ClassCastException at runtime.
  • Ensures type safety at compile time.
  • Code reusability with parameterized types.

2. How to Declare a Generic Class

A generic class defines a type parameter (T), which is replaced with a specific type at runtime.

Example: Creating a Generic Class

// Generic class with type parameter T
class Box<T> {
    private T value;

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

    public T get() {
        return value;
    }
}

public class GenericClassExample {
    public static void main(String[] args) {
        Box<String> strBox = new Box<>(); // Box of Strings
        strBox.set("Hello Generics");
        System.out.println(strBox.get()); // Output: Hello Generics

        Box<Integer> intBox = new Box<>(); // Box of Integers
        intBox.set(100);
        System.out.println(intBox.get()); // Output: 100
    }
}

🔹 Key Takeaway: T acts as a placeholder that is replaced with String, Integer, or any other type.


3. Generics in Methods

A generic method allows type parameters only for that method.

Example: Generic Method

class Utility {
    public static <T> void print(T item) {
        System.out.println(item);
    }
}

public class GenericMethodExample {
    public static void main(String[] args) {
        Utility.print("Hello"); // String
        Utility.print(123);     // Integer
        Utility.print(3.14);    // Double
    }
}

4. Generics in Interfaces

Example: Generic Interface

interface Container<T> {
    void set(T item);
    T get();
}

// Implementing the generic interface
class StringContainer implements Container<String> {
    private String item;

    public void set(String item) { this.item = item; }
    public String get() { return item; }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        Container<String> strContainer = new StringContainer();
        strContainer.set("Generic Interface");
        System.out.println(strContainer.get()); // Output: Generic Interface
    }
}

🔹 Key Takeaway: Generic interfaces allow flexibility in implementing classes.


5. Bounded Type Parameters

You can restrict generic types using bounded type parameters (extends keyword).

Example: Allow Only Number Types

class Calculator<T extends Number> { // T must be a subclass of Number
    private T num;

    public Calculator(T num) {
        this.num = num;
    }

    public double square() {
        return num.doubleValue() * num.doubleValue();
    }
}

public class BoundedGenericsExample {
    public static void main(String[] args) {
        Calculator<Integer> intCalc = new Calculator<>(5);
        System.out.println(intCalc.square()); // Output: 25.0

        Calculator<Double> doubleCalc = new Calculator<>(3.5);
        System.out.println(doubleCalc.square()); // Output: 12.25

        // Calculator<String> strCalc = new Calculator<>("Hello"); ❌ Compilation error
    }
}

🔹 Key Takeaway: <T extends Number> ensures T is only a Number type (Integer, Double, etc.).

6. Wildcards in Generics (?)

Wildcards (?) allow unknown types in generics.

Example: Unbounded Wildcard (?)

import java.util.*;

public class WildcardExample {
    public static void printList(List<?> list) { // Accepts any type of List
        for (Object item : list) {
            System.out.println(item);
        }
    }

    public static void main(String[] args) {
        List<String> strList = Arrays.asList("A", "B", "C");
        List<Integer> intList = Arrays.asList(1, 2, 3);

        printList(strList); // Works
        printList(intList); // Works
    }
}

🔹 Key Takeaway: List<?> means any type of List can be passed.

Example: Upper Bounded Wildcard (? extends T)

public class UpperBoundExample {
    public static double sum(List<? extends Number> list) { // Only Number or subclass
        double total = 0;
        for (Number num : list) {
            total += num.doubleValue();
        }
        return total;
    }

    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3);
        List<Double> doubleList = Arrays.asList(1.5, 2.5, 3.5);

        System.out.println(sum(intList)); // Output: 6.0
        System.out.println(sum(doubleList)); // Output: 7.5
    }
}

🔹 Key Takeaway: ? extends Number allows Integer, Double, etc.

Example: Lower Bounded Wildcard (? super T)

public class LowerBoundExample {
    public static void addNumbers(List<? super Integer> list) { // Only Integer or superclass (Number, Object)
        list.add(10);
    }

    public static void main(String[] args) {
        List<Number> numList = new ArrayList<>();
        addNumbers(numList);
        System.out.println(numList); // Output: [10]
    }
}

🔹 Key Takeaway: ? super Integer allows Integer and its superclasses (Number, Object).


7. Summary Table

FeatureDescriptionExample
Generic ClassClass with a type parameterclass Box<T> {}
Generic MethodMethod with a type parameter<T> void print(T item) {}
Generic InterfaceInterface with a type parameterinterface Container<T> {}
Bounded Type (extends)Restrict types to subclasses<T extends Number>
Unbounded Wildcard (?)Accepts any typeList<?>
Upper Bounded (? extends T)Accepts T and its subclassesList<? extends Number>
Lower Bounded (? super T)Accepts T and its superclassesList<? super Integer>
This entry was posted in Без рубрики. Bookmark the permalink.