How to Retrieve Generic Types at Runtime in Java: Tricks Beyond Type Erasure
This article explains how Java generics and reflection work together, why type erasure hides generic information at runtime, and demonstrates a practical technique using anonymous subclasses and the JVM's Signature attribute to recover the actual generic type.
Introduction
If you frequently write utility classes, you may need to obtain the runtime type of a generic parameter T. This article starts from Java generics, examines the reflection extensions for generics, and explores the impact of type erasure.
Generics
Generics were introduced in Java 1.5 to provide strong type checking and reusable code without the heap‑pollution problems of pre‑1.5 casts. The formal definition is in the Java Language Specification (JLS), and the informal definition can be illustrated with simple code examples.
/**
* Example generic class
* Type Parameter: T extends Number
* Type Variable: T
* Type Argument: Foo<Integer> contains Integer
*/
class Foo<T extends Number> {}Reflection
Since Java 1.5, reflection was extended with the Type interface and related classes to support generic types. The most relevant type for our purpose is ParameterizedType, which represents a concrete generic instance such as Foo<String> or Foo<Integer>.
Type Erasure
Java uses type erasure to maintain binary compatibility and avoid generating new bytecode for each generic instantiation. Consequently, generic information is removed at runtime, making it impossible to retrieve the actual type via ordinary reflection.
Generics are compile‑time only; the JVM cannot see the type parameters.
Therefore Class.getGenericSuperclass() returns null for generic type variables.
Technique: Using an Anonymous Subclass
By creating an anonymous subclass of a generic wrapper, the compiler records the concrete type in the class’s Signature attribute. This signature can later be read via reflection to obtain the runtime generic type.
class Wrapper<T> { }
public static Type getGenericRuntimeType(Wrapper<?> wrapper) {
Type type = wrapper.getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
return ((ParameterizedType) type).getActualTypeArguments()[0];
}
return null;
}
// Usage
Type t1 = getGenericRuntimeType(new Wrapper<List<String>>()); // null
Type t2 = getGenericRuntimeType(new Wrapper<List<String>>() {}); // java.util.List<java.lang.String>Compiler and JVM Details
The Java compiler writes generic signatures into the Signature attribute of the generated .class file. The JVM’s native method Class.getGenericSignature0() retrieves this attribute, and the reflection API parses it via ClassRepository.
Key steps:
During compilation, javac creates an abstract syntax tree (AST) and resolves generic types.
The ClassWriter writes the resolved generic signature into the class file.
At runtime, Class.getGenericSuperclass() calls getGenericInfo(), which invokes the native getGenericSignature0() to obtain the signature string.
The signature is parsed to produce a ParameterizedType instance.
Conclusion
By understanding Java’s generic type erasure, the compiler’s signature attribute, and the reflection API, we can recover generic type information at runtime using a simple anonymous‑class trick. This approach works without altering method signatures and is compatible with common serialization frameworks such as Jackson and Fastjson.
References
https://docs.oracle.com/javase/tutorial/java/generics/why.html
https://homepages.inf.ed.ac.uk/wadler/gj/Documents/gj-oopsla.pdf
https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.2
https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.4
https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.8.4
https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.1.2
https://docs.oracle.com/javase/1.5.0/docs/guide/reflection/enhancements.html
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/class-use/Type.html
https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.6
https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.5
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1
https://github.com/openjdk/jdk8u
https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html#heap_pollution
https://jcp.org/aboutJava/communityprocess/review/jsr014/index.html
https://openjdk.org/groups/compiler/doc/hhgtjavac/index.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Alibaba Cloud Developer
Alibaba's official tech channel, featuring all of its technology innovations.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
