Advanced Java August 05 ,2025

Generics Part- 1

1. Introduction to Generics

What Are Generics in Java?

Generics is a powerful feature introduced in Java 5 that allows you to define classes, interfaces, and methods with type parameters. Instead of writing code that works with specific data types, you write a single class or method that automatically works with any type.

Generic = Parameterized Type
This means you can create classes, interfaces, and methods where the type of data is specified as a parameter.

Why Use Generics?

Before Generics, Java developers used Object as a universal type. This caused issues like:

  • Explicit type casting
  • Run-time ClassCastException
  • Lack of compile-time type safety

Generics solve this problem by:

  • Enabling compile-time type checking
  • Eliminating the need for type casting
  • Making the code more reusable and readable

Example Without Generics (Pre-Java 5)

import java.util.ArrayList;

public class WithoutGenerics {
    public static void main(String[] args) {
        ArrayList list = new ArrayList(); // raw type
        list.add("Hello");
        list.add(100); // no compile-time error

        for (Object obj : list) {
            String str = (String) obj; // Runtime error: ClassCastException
            System.out.println(str);
        }
    }
}

Output

Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String

Explanation

  • The list contains both String and Integer types.
  • At runtime, trying to cast Integer to String throws ClassCastException.

Example With Generics

import java.util.ArrayList;

public class WithGenerics {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        list.add("Hello");
        // list.add(100); // Compile-time error

        for (String str : list) {
            System.out.println(str); // No casting required
        }
    }
}

Output

Hello

Explanation

  • The list is restricted to String type only.
  • Attempting to add an Integer will cause a compile-time error.
  • No need for explicit casting while iterating.

Advantages of Using Generics

FeatureDescription
Type SafetyDetects type mismatch errors at compile-time.
Code ReusabilityOne class or method works with multiple data types.
Eliminates Type CastingNo need to cast objects explicitly while retrieving.
Cleaner CodeMakes code easy to read, maintain, and debug.

Real-Life Analogy

Imagine a parcel box. Without generics, it’s like a box that can hold anything (you don’t know what’s inside until you open it). With generics, it's like labeling the box "Books only"—you know exactly what’s inside and won't mistakenly put in something else.

2. Generic Classes

What Are Generic Classes?

A generic class is a class that can operate on any type of data specified during instantiation. Instead of hardcoding the type, you define a type parameter that gets replaced when the class is used.

Syntax

class ClassName {
    // T is a type parameter
}
  • T is a type parameter (can be any valid identifier, though T, E, K, V, N are conventional).
  • This T can be used to declare variables, return types, parameters, etc.

Example: A Generic Box Class

class Box {
    private T value;

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

    public T getValue() {
        return value;
    }
}

Usage:

public class GenericClassExample {
    public static void main(String[] args) {
        Box stringBox = new Box<>();
        stringBox.setValue("Java Generics");
        System.out.println("String Value: " + stringBox.getValue());

        Box intBox = new Box<>();
        intBox.setValue(123);
        System.out.println("Integer Value: " + intBox.getValue());
    }
}

Output:

String Value: Java Generics
Integer Value: 123

Type Parameters Naming Convention

SymbolMeaning
TType
EElement (used in collections)
KKey
VValue
NNumber

These are not required by the language but are widely followed conventions.

Generic Class with Multiple Type Parameters

You can use more than one type parameter.

class Pair {
    private K key;
    private V value;

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

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

Usage:

public class MultiTypeExample {
    public static void main(String[] args) {
        Pair entry = new Pair<>("Age", 25);
        System.out.println("Key: " + entry.getKey());
        System.out.println("Value: " + entry.getValue());
    }
}

Output:

Key: Age
Value: 25

Raw Types vs Generic Types

Raw Type (Not Recommended):

Box rawBox = new Box();
rawBox.setValue("Hello");
String s = (String) rawBox.getValue(); // Explicit cast

Generic Type (Recommended):

Box stringBox = new Box<>();
stringBox.setValue("Hello");
String s = stringBox.getValue(); // No cast

Why Avoid Raw Types?

  • Type safety is lost.
  • Code is more error-prone.
  • No compile-time checking of types.

When to Use Generic Classes?

  • When your class needs to operate on various data types.
  • For collections, data structures, or utility classes.
  • When you want to enforce type consistency and reusability.

3. Generic Methods

What Are Generic Methods?

A generic method is a method that is declared with its own type parameters, independent of whether the class it belongs to is generic or not. These methods can work with different types without duplicating code.

Even in non-generic classes, you can define methods with type parameters.

Syntax

public  void methodName(T param) {
    // T can be used as parameter type, return type, etc.
}
  • The before the return type indicates that this is a generic method.
  • T is the type parameter, and can be replaced with any object type at method call time.

Example 1: Simple Generic Method

public class GenericMethodExample {
    public static  void display(T value) {
        System.out.println("Value: " + value);
    }

    public static void main(String[] args) {
        display("Hello Generics");
        display(123);
        display(45.67);
    }
}

Output:

Value: Hello Generics
Value: 123
Value: 45.67

Explanation:

  • The same method display is used for different types: String, Integer, Double.
  • Java infers the type from the argument.

Example 2: Generic Method with Return Type

public class ReturnGenericMethod {
    public static  T getFirstElement(T[] array) {
        return array[0];
    }

    public static void main(String[] args) {
        String[] names = {"John", "Jane", "Doe"};
        Integer[] numbers = {1, 2, 3};

        System.out.println("First Name: " + getFirstElement(names));
        System.out.println("First Number: " + getFirstElement(numbers));
    }
}

Output:

First Name: John
First Number: 1

Example 3: Generic Method with Multiple Type Parameters

public class MultiTypeMethod {
    public static  void printKeyValue(K key, V value) {
        System.out.println("Key: " + key + ", Value: " + value);
    }

    public static void main(String[] args) {
        printKeyValue("Name", "Alice");
        printKeyValue("Age", 30);
    }
}

Output:

Key: Name, Value: Alice
Key: Age, Value: 30

Generic Methods in Generic Classes

You can define a generic method inside a generic class. These may use new type parameters, different from the class's.

class Container {
    private T value;

    public Container(T value) {
        this.value = value;
    }

    public  void showAnotherType(U otherValue) {
        System.out.println("Container holds: " + value);
        System.out.println("Other value: " + otherValue);
    }
}

Usage:

public class Test {    
 public static void main(String[] args) {        Container stringContainer = new Container<>("Hello");        stringContainer.showAnotherType(100);    } }

Output:

Container holds: Hello Other value: 100

Type Inference in Generic Methods

When calling a generic method, the compiler usually infers the type from the arguments. However, you can explicitly provide it:

 T identity(T value);

String result = GenericClass.identity("Test");

Usually, writing is optional because the compiler can infer it automatically.

When to Use Generic Methods?

  • When a method needs to operate on different types.
  • When you want type safety without duplicating code.
  • In utility/helper classes like Collections or Arrays.

4. Bounded Type Parameters in Java

What Are Bounded Type Parameters?

In Java Generics, we can restrict the kind of types that can be used as type arguments. This is known as a bounded type parameter.

By default, a type parameter (like ) can be any type—Object, String, Integer, custom classes, etc.
But sometimes, we want to restrict T to a certain type or family of types.

In such cases, we use bounds.

4.1. Upper Bounded Type Parameters:

Definition

To restrict the type parameter to a specific class or its subclasses, we use:

 

This means T must be either SomeClass or a subclass of SomeClass.

Use Case Example: Working Only with Numbers

Let’s say we want to write a method that calculates the average of a list of numbers (like Integer, Float, Double). Since they all inherit from Number, we can use:

public class UpperBoundExample {
    public static  double calculateAverage(T[] numbers) {
        double sum = 0.0;
        for (T num : numbers) {
            sum += num.doubleValue(); // convert each to double
        }
        return sum / numbers.length;
    }

    public static void main(String[] args) {
        Integer[] intArray = {10, 20, 30};
        Double[] doubleArray = {12.5, 15.5, 20.0};

        System.out.println("Integer Avg: " + calculateAverage(intArray));
        System.out.println("Double Avg: " + calculateAverage(doubleArray));
    }
}

Output:

Integer Avg: 20.0
Double Avg: 16.0

4.2. Multiple Bounds:

You can apply multiple bounds, but:

  • The first bound must be a class (if any),
  • The rest must be interfaces.

Syntax

 

Example

interface Printable {
   void print();
}
class Report {
   void generate() {
       System.out.println("Generating report...");
   }
}
class AnnualReport extends Report implements Printable {
   public void print() {
       System.out.println("Printing Annual Report");
   }
}
public class MultipleBoundExample {
   public static  void process(T item) {
       item.generate();
       item.print();
   }
   public static void main(String[] args) {
       AnnualReport report = new AnnualReport();
       process(report);
   }
}

Output:

Generating report...
 Printing Annual Report

4.3. Lower Bounded Type Parameters: (Using Wildcards)

Lower bounds are used more with wildcards than with generic method declarations. But let’s understand it here as it’s related.

This allows you to write code that works with a class and its superclasses.

More on this will be covered in the next chapter: Wildcards in Generics, but here's a preview:

Example

public class LowerBoundExample {
    public static void addNumbers(List list) {
        list.add(10); // allowed
        list.add(20);
        // list.add("Hello"); // Error: not an Integer or its subclass
    }

    public static void main(String[] args) {
        List numbers = new ArrayList<>();
        addNumbers(numbers);
        System.out.println(numbers);
    }
}

Output:

[10, 20]

When Should You Use Bounded Type Parameters?

Use CaseBound Type
You want to operate only on subtypes of a classextends
You want to create flexible write-only collectionssuper
You want to enforce multiple behaviors in one typeMultiple bounds

Common Real-Life Examples in Java API

  • public class Enum>
  • public interface Comparable
  • Collections.max(Collection)

These bounds ensure that certain operations (like comparison or ordering) are guaranteed to be supported by the type used.

5. Wildcards in Generics

What Are Wildcards in Java Generics?

In Java, wildcards are represented by the symbol ? and are used to make generic code more flexible.

Wildcards allow us to write code that works with a range of types, rather than a specific one. This is especially useful when you’re working with collections and don’t want to lock your logic to one strict type.

Basic Syntax




5.1. Unbounded Wildcard:

Meaning:

Accepts any type — it is the most general form of a wildcard.

Use Case:

When you want to read values from a generic structure but don't care about the actual type.

Example:

import java.util.*;

public class UnboundedWildcardExample {
    public static void printList(List list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }

    public static void main(String[] args) {
        List stringList = Arrays.asList("Java", "Python", "C++");
        List intList = Arrays.asList(1, 2, 3);

        printList(stringList);
        printList(intList);
    }
}

Output:

Java
Python
C++
1
2
3

You can read from List but cannot add elements to it (except null) because the actual type is unknown.

5.2. Upper Bounded Wildcard:

Meaning:

Accepts a type T or any of its subclasses.

Use Case:

When you want to read from a collection of type T or its subtypes (e.g., Integer, Double, etc., are subtypes of Number).

Example:

import java.util.*;

public class UpperBoundedWildcardExample {
    public static double sum(List numbers) {
        double total = 0;
        for (Number num : numbers) {
            total += num.doubleValue();
        }
        return total;
    }

    public static void main(String[] args) {
        List intList = Arrays.asList(10, 20, 30);
        List doubleList = Arrays.asList(10.5, 20.5, 30.0);

        System.out.println("Sum of integers: " + sum(intList));
        System.out.println("Sum of doubles: " + sum(doubleList));
    }
}

Output:

Sum of integers: 60.0
Sum of doubles: 61.0

You can read from ? extends T but cannot add values because Java doesn't know the exact subtype.

5.3. Lower Bounded Wildcard:

Meaning:

Accepts a type T or any of its superclasses.

Use Case:

When you want to write to a generic collection of type T or its superclasses.

Example:

import java.util.*;

public class LowerBoundedWildcardExample {
    public static void addNumbers(List list) {
        list.add(10);
        list.add(20);
    }

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

Output:

[10, 20]

You can add to ? super T but when you retrieve, the type is Object.

5.4. The PECS Principle

A famous rule to remember:

PECS = Producer Extends, Consumer Super

WildcardCan ReadCan WriteMeaning
 ✅ Yes❌ NoAccepts T and its subclasses (producer)
 ❌ Limited✅ YesAccepts T and its superclasses (consumer)
 ✅ Yes❌ NoAccepts any type (read-only)

Common Use Cases Comparison

Wildcard TypeWhen to Use
 You just want to read, and type is irrelevant
 You want to read from a producer (e.g., sum of numbers)
 You want to add elements to a consumer (e.g., write integers to a list)

Invalid Operations with Wildcards

You cannot do the following:

List list = new ArrayList();
list.add(10); // Error

You can only read from ? extends Type, not write (except null).

Real-Life Analogy

  • → A fruit basket that only accepts fruits (Apple, Mango, etc.). You can take fruits out, but you can't put anything in (except another fruit).
  • → A basket that can accept Apple or its supertypes (like Fruit or Object). You can safely put Apples into it.

6. Type Erasure in Java

What Is Type Erasure?

Type Erasure is a process by which the Java compiler removes all generic type information during compilation.

Generics are a compile-time feature only. After the Java compiler checks types, it replaces generic types with their upper bounds or Object. This means:

At runtime, there is no generic type information available.

Why Does Java Use Type Erasure?

Java maintains backward compatibility with older versions (Java 1.4 and below), which didn’t have generics. This allows generic code to run on JVMs that don’t know about generics.

So instead of creating new bytecode instructions, the compiler "erases" the generic type and uses raw types internally.

How Type Erasure Works

Let’s see it in action.

Example Before Compilation:

class Box {
    private T value;

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

    public T get() {
        return value;
    }
}

After Compilation (Simplified View):

class Box {
    private Object value;

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

    public Object get() {
        return value;
    }
}

All instances of T are replaced with Object (or the upper bound if specified).

Examples Demonstrating Type Erasure

Example 1: No Runtime Type Information

import java.util.*;

public class TypeErasureDemo {
    public static void main(String[] args) {
        List list1 = new ArrayList<>();
        List list2 = new ArrayList<>();

        System.out.println(list1.getClass() == list2.getClass()); // true
    }
}

Output:

true

Explanation:

At runtime, both List and List are just ArrayList. The generic type information is erased.

Implications of Type Erasure

Because of type erasure, certain operations are not allowed in Java Generics:

✅ Allowed:

List list = new ArrayList<>();
String str = list.get(0); // works, checked at compile-time

❌ Not Allowed:

1. Cannot Use instanceof with Parameterized Type

if (obj instanceof List) { } // Compile-time error

You can only use:

if (obj instanceof List) { } // OK

2. Cannot Create Generic Arrays

List[] array = new ArrayList[10]; // Error

Why? Because the JVM doesn’t retain generic type info, and arrays require reifiable types (types available at runtime).

3. Cannot Instantiate Generic Types

class Container {
    T value = new T(); // Error: Cannot instantiate type parameter
}

Because type T is erased to Object, and Java doesn’t know what constructor to call.

4. Cannot Use Generics in Static Contexts

class Example {
    static T staticVar; // Error
}

Static members belong to the class, not to a specific instance. But T only exists at the instance level, so this is not allowed.

Bridge Methods (Compiler-Generated)

Due to type erasure, sometimes the compiler adds bridge methods to preserve polymorphism.

Example:

class Parent {
    T getValue() { return null; }
}

class Child extends Parent {
    String getValue() { return "Hello"; }
}

To maintain correct overriding after erasure, the compiler creates a bridge method in Child that looks like:

Object getValue() {
   return getValue(); // calls actual String-returning method
}

7. Generic Interfaces in Java

What Are Generic Interfaces?

Just like classes and methods, interfaces in Java can also be generic. This allows interfaces to be type-safe and flexible, enabling them to work with different data types without rewriting code.

A generic interface declares one or more type parameters that are defined when the interface is implemented or extended.

Syntax of a Generic Interface

interface InterfaceName {
   void method(T param);
}

Here, T is a type parameter that will be replaced by a concrete type (e.g., String, Integer, CustomClass) when the interface is implemented.

7.1. Example 1: Basic Generic Interface

interface Processor {
    void process(T value);
}

class StringProcessor implements Processor {
    public void process(String value) {
        System.out.println("Processing string: " + value.toUpperCase());
    }
}

class IntegerProcessor implements Processor {
    public void process(Integer value) {
        System.out.println("Processing integer: " + (value * value));
    }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        Processor stringProcessor = new StringProcessor();
        stringProcessor.process("hello");

        Processor intProcessor = new IntegerProcessor();
        intProcessor.process(10);
    }
}

Output:

Processing string: HELLO
Processing integer: 100

7.2. Generic Interface with Multiple Type Parameters

interface Pair {
   K getKey();
   V getValue();
}
class KeyValue implements Pair {
   private K key;
   private V value;
   public KeyValue(K key, V value) {
       this.key = key;
       this.value = value;
   }
   public K getKey() { return key; }
   public V getValue() { return value; }
}

Usage:

public class PairExample {
   public static void main(String[] args) {
       Pair entry = new KeyValue<>("Age", 25);
       System.out.println("Key: " + entry.getKey());
       System.out.println("Value: " + entry.getValue());
   }
}

Output:

Key: Age 
Value: 25

7.3. Implementing Generic Interface with Raw Types (Not Recommended)

You can choose not to specify type parameters, but this leads to raw types and disables compile-time type checking:

class RawProcessor implements Processor {
    public void process(Object value) {
        System.out.println("Raw processing: " + value);
    }
}

This is discouraged, as it defeats the purpose of generics.

7.4. Generic Interface with Anonymous Classes or Lambda (Functional Interface)

If your interface is a functional interface (i.e., has one abstract method), you can implement it using lambda expressions (Java 8+).

Example:

interface Transformer {
   T transform(T input);
}
public class LambdaGenericExample {
   public static void main(String[] args) {
       Transformer toUpper = str -> str.toUpperCase();
       System.out.println(toUpper.transform("java"));
       Transformer square = x -> x * x;
       System.out.println(square.transform(5));
   }
}

Output:

JAVA
25

7.5. Why Use Generic Interfaces?

BenefitExplanation
Type SafetyPrevents class cast exceptions at runtime
Code ReusabilityOne interface can work with multiple data types
Flexible ArchitectureEasily adapt implementations for different object types
Integration with Functional APIsCombine with lambdas and streams for powerful constructs

Real-World Usage Examples

Generic interfaces are widely used in the Java Standard Library, such as:

InterfaceDeclaration
Comparableint compareTo(T o)
Comparatorint compare(T o1, T o2)
IterableIterator iterator()
List, MapCollections framework

8. Generic Constructors in Java

What Are Generic Constructors?

In Java, a generic constructor is a constructor that defines its own type parameters, independently of the type parameters of the class it belongs to.

Even if a class is not generic, a constructor inside it can be generic.

Syntax

class ClassName {
     ClassName(T param) {
        // constructor body
    }
}

Note:

  • The type parameter is declared before the constructor name, similar to how it’s declared for a generic method.
  • This is independent of whether the class is generic.

8.1. Example 1: Generic Constructor in Non-Generic Class

class Printer {
   public  Printer(T data) {
       System.out.println("Printing data: " + data);
   }
}

Usage:

public class GenericConstructorDemo {
    public static void main(String[] args) {
        Printer p1 = new Printer("Hello World");
        Printer p2 = new Printer(100);
        Printer p3 = new Printer(99.99);
    }
}

Output:

Printing data: Hello World
Printing data: 100
Printing data: 99.99

8.2. Example 2: Generic Constructor in Generic Class

You can also have a generic constructor in a class that’s already generic — with its own type parameters.

class DataHolder {
    private T value;

    public DataHolder(T value) {
        this.value = value;
    }

    // Generic constructor with a different type parameter U
    public  DataHolder(U input, T value) {
        System.out.println("Input received: " + input);
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

Usage:

public class DualGenericConstructor {    public static void main(String[] args) {        DataHolder d1 = new DataHolder<>("Hello");        DataHolder d2 = new DataHolder<>(12345, "World");        System.out.println("Value from d1: " + d1.getValue());        System.out.println("Value from d2: " + d2.getValue());    } }

Output:

Value from d1: Hello Input received: 12345 Value from d2: World

8.3. Use Case: Type Conversion or Logging

Generic constructors are often used when:

  • You want to accept different types of input
  • You need to perform type inspection, conversion, or logging
  • You don’t want to make the entire class generic

8.4. Important Notes

PointExplanation
Declared before constructorRequired to indicate it’s a generic constructor
Can be used in generic/non-generic classIndependent of class’s type parameters
Can use different type variablesin constructor is not same as class’s
Type inference worksJava can infer the type argument at call site

8.5. Example: Constructor That Converts Type

class Converter {    public  Converter(T input) {        System.out.println("Converted to string: " + input.toString());    } }

Usage:

public class ConversionDemo {    public static void main(String[] args) {        Converter c1 = new Converter(10);           // Integer        Converter c2 = new Converter(45.6);         // Double        Converter c3 = new Converter("Generics");   // String    } }

Output:

Converted to string: 10 Converted to string: 45.6 Converted to string: Generics

When to Use Generic Constructors?

  • When the constructor operates on different types than the class.
  • When you need a flexible constructor without generifying the entire class.
  • For utility, conversion, or logging classes that handle multiple data types at creation.

9. Using Generics with the Collections Framework

Why Collections Use Generics

Before Java 5, Java collections like List, Set, and Map held raw objects, which meant:

  • You could insert any object into them.
  • You needed to cast the object when retrieving.
  • Type mismatches were caught at runtime, not compile time.

Java 5 introduced Generics into the Collections Framework to provide:

  • Type safety
  • Cleaner, more readable code
  • Compile-time checks

9.1. Without Generics (Raw Types)

import java.util.*; public class RawListExample {    public static void main(String[] args) {        List list = new ArrayList(); // raw type        list.add("Java");        list.add(123); // no error        String value = (String) list.get(0); // casting required        System.out.println(value);        // This will throw ClassCastException        String value2 = (String) list.get(1); // runtime error        System.out.println(value2);    } }

Output:

Java Exception in thread "main" java.lang.ClassCastException

9.2. With Generics

import java.util.*; public class GenericListExample {    public static void main(String[] args) {        List list = new ArrayList<>();        list.add("Java");        // list.add(123); // Compile-time error        for (String item : list) {            System.out.println(item); // no casting        }    } }

Output:

Java

9.3. Generic Interfaces in Collections

Here’s how generics are used in some key collection interfaces:

InterfaceDeclaration
ListRepresents an ordered collection of elements
SetRepresents a collection with no duplicates
MapStores key-value pairs
IteratorIterates over a collection of elements
ComparableCompares objects of a specific type
ComparatorCompares two different objects of type T

9.4. List Example

import java.util.*; public class ListWithGenerics {    public static void main(String[] args) {        List numbers = new ArrayList<>();        numbers.add(5);        numbers.add(10);        numbers.add(15);        for (Integer num : numbers) {            System.out.println("Square: " + (num * num));        }    } }

Output:

Square: 25 Square: 100 Square: 225

9.5. Map Example

import java.util.*; public class MapWithGenerics {    public static void main(String[] args) {        Map ageMap = new HashMap<>();        ageMap.put("Alice", 25);        ageMap.put("Bob", 30);        for (Map.Entry entry : ageMap.entrySet()) {            System.out.println(entry.getKey() + " -> " + entry.getValue());        }    } }

Output:

Alice -> 25 Bob -> 30

9.6. Custom Class in a Generic Collection

class Student {    String name;    int marks;    Student(String name, int marks) {        this.name = name;        this.marks = marks;    }    public String toString() {        return name + ": " + marks;    } }

Usage:

import java.util.*; public class CustomClassExample {    public static void main(String[] args) {        List students = new ArrayList<>();        students.add(new Student("Aman", 90));        students.add(new Student("Riya", 85));        for (Student s : students) {            System.out.println(s);        }    } }

Output:

Aman: 90 Riya: 85

9.7. Iterator with Generics

import java.util.*; public class IteratorExample {    public static void main(String[] args) {        List names = Arrays.asList("John", "Jane", "Jake");        Iterator iterator = names.iterator();        while (iterator.hasNext()) {            System.out.println(iterator.next());        }    } }

 

9.8. Benefits of Using Generics in Collections

BenefitExplanation
Type SafetyPrevents inserting the wrong data types
No CastingNo need for casting during retrieval
Compile-Time ChecksCatches type errors before code is run
Cleaner CodeMore readable and less error-prone

9.9. Common Mistakes and Best Practices

MistakeWhy It's a ProblemBest Practice
Using raw types (List list)No type safety, prone to runtime errorsAlways use parameterized types
Mixing types in the same listLeads to ClassCastExceptionKeep type consistent
Casting values on retrievalRedundant and unsafeUse generics to avoid casting

Summary

We explored the fundamentals of Java Generics — starting from why they were introduced, their advantages (type safety, reusability, no casting), and how they work in classes, methods, and interfaces. We also covered bounded type parameters, wildcards (? extends, ? super), type erasure, generic constructors, and their usage with the Collections Framework.

Continue in Part 2...

Next Blog- Generics Part- 2

Sanjiv
0

You must logged in to post comments.

Get In Touch

123 Street, New York, USA

+012 345 67890

techiefreak87@gmail.com

© Design & Developed by HW Infotech