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>
andList<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).