What is a `Class<T>` type token, and how does it let generic code work around erasure?
answer
- Class<T> (e.g. String.class) survives erasure
- Enables newInstance, cast, isInstance, Array.newInstance
- type.cast() = checked cast, fails immediately
- Typesafe heterogeneous container: Map<Class<?>, Object>
- Can't capture List<String>.class -> super type tokens
basics
~20 sA type token is just a Class<T> object (like String.class) that you pass into generic code. Because the Class object still knows the concrete type at runtime, the code can use it to create instances, cast, or build arrays even though the type parameter T itself was erased.
solid answer
~40 sType erasure removes the type argument at runtime, so generic code can't directly act on T. A *type token* restores that lost information: the caller passes a `Class<T>` (e.g. `String.class`), which is a runtime object that knows exactly what class it represents. The generic method can then do erasure-defeating work through it: `clazz.getDeclaredConstructor().newInstance()` to build a T, `clazz.cast(obj)` for a checked cast, or `Array.newInstance(clazz, n)` for a properly-typed array. This is the basis of `Class.cast`, `EnumSet.noneOf(Class)`, and *typesafe heterogeneous containers* (e.g. `Map<Class<?>, Object>` keyed by type, as in `Effective Java` Item 33). The cost is that the API must demand the token and it leans on reflection, so failures (missing constructor, access) appear at runtime. Use it when you genuinely need the runtime class; otherwise a `Supplier<T>` factory is simpler and fully compile-time safe.
code
java · 11 lines// Type token recovers runtime type that erasure removed:
static <T> T firstInstance(java.util.List<?> items, Class<T> type) {
for (Object o : items) {
if (type.isInstance(o)) { // erasure-safe 'instanceof T'
return type.cast(o); // checked cast, throws immediately if wrong
}
}
return null;
}
String s = firstInstance(List.of(1, "two", 3.0), String.class); // "two"go deeper
Recognizes passing SomethingClass.class into a method (like Jackson's readValue) and that it tells the code the concrete type.
Explains that Class<T> survives erasure and uses it to call newInstance/cast/isInstance; can write a method that takes a Class<T> and returns a T.
Knows type.cast vs plain cast semantics, the typesafe heterogeneous container pattern, EnumSet.noneOf, and the List<String>.class limitation requiring super type tokens.
Weighs token-based reflective APIs against factory-based ones in framework design, understands super-type-token mechanics and their reflective cost, and the soundness boundaries of erasure-era APIs.
## The gap erasure leaves Because Java **erases** generic type arguments, code written against a type parameter `T` has no way, at runtime, to find out what `T` actually is. It can't do `new T()`, `new T[]`, `obj instanceof T`, or `(T)` as a *checked* cast. A **type token** is the standard way to plug that gap. ## What a type token is Every class in Java has a corresponding `Class` object, reachable via the `.class` literal: `String.class` has type `Class<String>`, `Integer.class` has type `Class<Integer>`, and so on. This `Class<T>` object is a real runtime object that *does* know precisely which class it represents — erasure doesn't touch it. Passing such an object into generic code is the **type token pattern**: the caller supplies the runtime identity of `T` that the code otherwise lost. ```java static <T> T parse(String json, Class<T> type) { ... } User u = parse(text, User.class); // User.class is the token; T is inferred as User ``` The compiler infers `T = User` from `User.class` (of type `Class<User>`), and inside the method `type` is a live handle to the `User` class. ## What the token lets you do Given `Class<T> type`, you can perform several operations that bare `T` cannot: - **Instantiate:** `type.getDeclaredConstructor().newInstance()` returns a `T` (reflection; needs an accessible matching constructor). *(`Class.newInstance()` is deprecated since Java 9.)* - **Checked cast:** `type.cast(someObject)` casts to `T` and throws `ClassCastException` *immediately* if wrong — unlike a plain `(T)` cast, which erasure turns into a no-op that fails later. - **Runtime type test:** `type.isInstance(obj)` is the erasure-safe equivalent of `obj instanceof T`. - **Typed array:** `Array.newInstance(type, n)` builds an array whose real component type is `T`. ## A canonical use: typesafe heterogeneous container Normally a container is parameterized by one type (`List<String>`). Sometimes you want a container holding values of *many* different types, each retrievable with full type safety. The trick (Effective Java Item 33) is to key a map by the `Class` token itself: ```java class Favorites { private final Map<Class<?>, Object> m = new HashMap<>(); <T> void put(Class<T> type, T instance) { m.put(type, type.cast(instance)); } <T> T get(Class<T> type) { return type.cast(m.get(type)); } } Favorites f = new Favorites(); f.put(String.class, "hi"); f.put(Integer.class, 42); String s = f.get(String.class); // type-safe, no cast at the call site ``` The `Class<T>` keys carry the per-entry type, and `type.cast` re-establishes type safety on the way out. This is exactly how `EnumSet.noneOf(elementType)` and many framework APIs (Jackson `readValue(json, Class<T>)`, Spring `getBean(Class<T>)`) work. ## Limits and the alternative Tokens rely on **reflection**, so they can fail at runtime (no accessible no-arg constructor, security/module access). They also can't capture a *generic* type fully — `List<String>.class` doesn't exist, because at runtime there's only `List.class` (this is why libraries add a `TypeReference`/`TypeToken` super-type-token hack to capture full generic types). When you only need to *construct* a `T`, a **`Supplier<T>`** is simpler, compile-time safe, and supports constructor arguments. Reach for `Class<T>` when you actually need the runtime class — for casting, instance checks, reflective array creation, or as a map key. ## Key takeaway A type token is a `Class<T>` you pass in so erased generic code can recover the runtime type — to instantiate, cast safely, test instances, or build typed arrays. It trades compile-time guarantees for reflective flexibility; prefer a factory when construction is all you need.
- How does `type.cast(x)` differ from a plain `(T) x` cast?Class.cast performs a real runtime check and throws ClassCastException right there if x isn't a T. A plain (T) cast is erased to (effectively) nothing, so the check is deferred and may fail far away as 'unexpected' heap pollution.
- Why can't a single `Class<T>` token capture `List<String>`?At runtime only List.class exists — the <String> is erased — so there's no List<String>.class literal. Libraries work around this with super type tokens (e.g. Jackson's TypeReference, Guava's TypeToken) that capture the generic type via an anonymous subclass.
saying these in an interview costs you the question
- Thinking the token magically un-erases T everywhere (it only helps where you pass/use it)
- Believing you can write List<String>.class
- Treating a plain (T) cast as equally safe as Class.cast
- Ignoring that newInstance needs an accessible constructor and throws at runtime