Java Generics: Understanding Type Parameters and Wildcards

Java Generics: Understanding Type Parameters and Wildcards

Generic Types and Inheritance

In Java, we can define classes with generic type parameters to create flexible and type-safe code. Consider the following class hierarchy: GrandParent, Parent, Child, and an unrelated class Person.

import java.util.ArrayList;

public class GenericDemo {
    public static void main(String[] args) {
        // Create lists with different types
        ArrayList<GrandParent> list1 = new ArrayList<>();
        ArrayList<Parent> list2 = new ArrayList<>();
        ArrayList<Child> list3 = new ArrayList<>();
        ArrayList<Person> list4 = new ArrayList<>();
        
        // Test the generic method
        processCollection(list1);
        processCollection(list2);
        processCollection(list3);
        
        // This would cause a compilation error:
        // processCollection(list4);
    }
    
    /**
     * This method demonstrates generic wildcards.
     * Benefits: Ensures type consistency when adding elements
     * Limitation: Can accept any data type that matches the wildcard constraint
     * Solution: Use wildcards to restrict acceptable types:
     *   ? extends E: Accepts E or any of its subclasses
     *   ? super E: Accepts E or any of its superclasses
     * Use cases:
     *   1. When type is uncertain, define generic classes, methods, or interfaces
     *   2. When working with a specific inheritance hierarchy, use wildcards
     */
    public static void processCollection(ArrayList<? extends GrandParent> collection) {
        // Method implementation
    }
}

class GrandParent {
    // GrandParent class implementation
}

class Parent extends GrandParent {
    // Parent class implementation
}

class Child extends Parent {
    // Child class implementation
}

class Person {
    // Unrelated class implementation
}

Generic Methods

If we define a generic method instead, we can make it more flexible:

public static <T> void processCollection(ArrayList<T> collection) {
    // Generic method implementation
}

How ever, this approach would accept any type, reducing type specificity. That's why wildcards are useful for more constrained scenairos.

Wildcard Practice Exercise

Let's consider a practical example with animal types:

public class AnimalKeeper {
    public static void main(String[] args) {
        ArrayList<SiameseCat> cats1 = new ArrayList<>();
        ArrayList<MaineCoonCat> cats2 = new ArrayList<>();
        ArrayList<BeagleDog> dogs1 = new ArrayList<>();
        ArrayList<GoldenRetriever> dogs2 = new ArrayList<>();
        
        // Only cat collections can be processed
        careForPets(cats1);
        careForPets(cats2);
        
        // This would cause compilation error:
        // careForPets(dogs1);
    }
    
    public static void careForPets(ArrayList<? extends Cat> pets) {
        // Iterate through the collection and call the eat method
        // Only cats are allowed, not dogs
        for (Cat pet : pets) {
            pet.eat();
        }
    }
}

// Animal hierarchy
class Animal {
    public void eat() {
        System.out.println("Animal is eating");
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("Cat is eating fish");
    }
}

class SiameseCat extends Cat {
    // Siamese cat specific behavior
}

class MaineCoonCat extends Cat {
    // Maine Coon cat specific behavior
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating meat");
    }
}

class BeagleDog extends Dog {
    // Beagle dog specific behavior
}

class GoldenRetriever extends Dog {
    // Golden Retriever specific behavior
}

Frequently Asked Questions

1. What are Java Generics?

Generics were introduced in JDK 5 as a feature that allows us to constrain the types of operations at compile time and perform type checking.

2. What are the benefits of using generics?

  • Ensures consistent data types throughout collections
  • Moves potential runtime errors to compile time, preventing certain types of exceptions

3. What are important details about generics?

  • Primitive data types (bytee, short, int, long, double, boolean, char, float) cannot be used as type parameters
  • When a specific generic type is specified, you can pass that type or its subtypes
  • If no generic type is specified, the type defaults to Object

Tags: java-generics type-parameters wildcards type-safety Inheritance

Posted on Fri, 16 Oct 2026 16:15:04 +0000 by sparshdharam