Generic Reflection

Generic Reflection refers to using the Java Reflection API to inspect and analyze generic types (like List<String>, Map<String, Integer>, etc.) at runtime.

Normally, due to type erasure, generic type information is not available at runtime. However, some type metadata is preserved and can be accessed using the java.lang.reflect package — particularly through Type, ParameterizedType, and TypeVariable.

Key Classes in Generic Reflection

Class/Interface Purpose
Class<?> Used to access runtime class metadata
Type General interface for all type representations
ParameterizedType Represents a parameterized type like List<String>
TypeVariable Represents type variables (e.g., T in class Box<T>)
GenericArrayType Represents generic array types

Program: Reflecting on a Generic Class

import java.lang.reflect.*;

class Box<T> {
    private T item;

    public void setItem(T item) { this.item = item; }
    public T getItem() { return item; }
}

public class GenericReflectionDemo {
    public static void main(String[] args) {
        Class<?> clazz = Box.class;

        // Print declared type parameters
        TypeVariable<?>[] typeParams = clazz.getTypeParameters();
        System.out.println("Type Parameters of Box:");
        for (TypeVariable<?> t : typeParams) {
            System.out.println("Name: " + t.getName());
            System.out.println("Bounds: " + java.util.Arrays.toString(t.getBounds()));
        }
    }
}
/*
Type Parameters of Box:
Name: T
Bounds: [class java.lang.Object]
*/

Program-2: Getting Generic Parameter Types of Fields or Methods

import java.lang.reflect.*;
import java.util.List;

class EmployeeHolder {
    public List<String> names;
}

public class GenericFieldDemo {
    public static void main(String[] args) throws Exception {
        Field field = EmployeeHolder.class.getField("names");
        Type type = field.getGenericType();

        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            System.out.println("Raw Type: " + pt.getRawType());
            System.out.println("Actual Type Argument: " + pt.getActualTypeArguments()[0]);
        }
    }
}
/*
Raw Type: interface java.util.List  
Actual Type Argument: class java.lang.String
*/

Program-3: Reflecting on a Method’s Generic Return and Parameter Types

import java.lang.reflect.*;
import java.util.*;

class Service {
    public Map<String, List<Integer>> getData(String key) { return null; }
}

public class GenericMethodReflection {
    public static void main(String[] args) throws Exception {
        Method method = Service.class.getMethod("getData", String.class);
        Type returnType = method.getGenericReturnType();

        if (returnType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) returnType;
            System.out.println("Return Type: " + pt.getRawType());

            for (Type arg : pt.getActualTypeArguments()) {
                System.out.println("Type Argument: " + arg);
            }
        }
    }
}
/*
Return Type: interface java.util.Map  
Type Argument: class java.lang.String  
Type Argument: java.util.List<java.lang.Integer>
*/

Program-4: Custom Generic Class with Reflection

class Pair<K, V> {
    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; }
}

public class PairReflection {
    public static void main(String[] args) {
        TypeVariable<?>[] typeVars = Pair.class.getTypeParameters();
        for (TypeVariable<?> var : typeVars) {
            System.out.println("Name: " + var.getName());
            System.out.println("Generic Declaration: " + var.getGenericDeclaration());
        }
    }
}
/*
Name: K  
Generic Declaration: class Pair  
Name: V  
Generic Declaration: class Pair
*/

Limitations of Generic Reflection

  • Type erasure removes most generic type information at runtime

  • You cannot distinguish between List<String> and List<Integer> at runtime

  • Generic types used only in method bodies are not accessible through reflection

Generic reflection is a powerful feature in Java that allows developers to inspect and analyze generic types at runtime using the Reflection API. While Java’s type erasure removes most of the generic type information during compilation, critical metadata — such as type parameters, parameterized types, and method signatures — can still be accessed through interfaces like Type, ParameterizedType, and TypeVariable.

Using generic reflection, developers can:

  • Examine type parameters of generic classes.

  • Access actual type arguments of generic fields and return types.

  • Analyze generic method signatures to build dynamic and reusable code (e.g., serialization tools, dependency injectors, or type-safe frameworks).

Scroll to Top