← Back to list

The Day I Finally Understood Java Type Erasure (Thanks to a RestTemplate Error)

I had heard the term “Type Erasure” many times before. Interview questions mention it. Java books mention it. Blog posts mention it.

Ashim Roy · 2026-06-18 15:59 · 0 claps · 4.1 min read
#java-generic #type-erasure #resttemplate #springboot-3 #java8
Open on Medium ↗

The Day I Finally Understood Java Type Erasure (Thanks to a RestTemplate Error)

I had heard the term “Type Erasure” many times before. Interview questions mention it. Java books mention it. Blog posts mention it.

But honestly, I never had a practical reason to care about it. That changed when I was building a simple Spring Boot Product Service and hit a strange problem.

I wanted to fetch products from FakeStore API:

List<FakeStoreProductDto> fakeStoreProductDtos =
        restTemplate.getForObject(
                "https://fakestoreapi.com/products/",
                List<FakeStoreProductDto>.class
        );

Looks reasonable, right?

Except it doesn’t work.

And understanding why leads directly to one of the most important concepts in Java Generics: Type Erasure.

Before Java Generics

To understand Type Erasure, we first need to understand why Generics were introduced at all. Before Java 5, collections looked like this:

List list = new ArrayList();

This is called a raw type.

Since we haven’t specified any type, Java allows us to store anything inside the list:

list.add(new Student());
list.add(new Animal());
list.add("Hello");
list.add(10);

At first glance, this looks very flexible. But the problem appears when we try to read data back from the list. Suppose I know that the third element is supposed to be a Student. The problem appeared when retrieving data:

Student student = (Student) list.get(0);

Notice the typecast.

Since Java doesn’t know what is stored inside the list, we have to manually tell it:

“Trust me, this is a Student.”

And if the object wasn’t actually a Student? But what if we are wrong? What if the third element is actually a String or an Integer?

Student student = (Student) list.get(2);

Now the code compiles successfully, but fails at runtime with:

ClassCastException

Runtime failure. This was one of the biggest problems before Generics.

Java couldn’t protect us. There was no type safety. Java couldn’t prevent developers from putting the wrong objects into a collection.

And even worse, many mistakes were discovered only when the application was running. Imagine finding such an error in production.

Generics Solved This

To solve this problem, Java 5 introduced Generics. Now we can write:

List<Student> students = new ArrayList<>();

Here, Student becomes the type parameter. Think of T in:

List<T>

as a placeholder. When we write:

List<Student>

Java replaces T with Student. Only Student objects are allowed. Now the compiler knows exactly what kind of objects are allowed inside the collection. If someone tries to add something else:

This immediately gives us compile-time safety.

students.add(new Animal());
students.add("Hello");
students.add(10);

Compile error. the code won’t even compile. The error is caught immediately.

This is called Compile-Time Type Safety.

Instead of discovering problems at runtime, Java tells us about them while writing the code.

That is the single biggest reason Generics were introduced. Much better than finding the problem in production.

The Interesting Problem

Java has a very important design principle:

Java has always been a backward-compatible language. Backward Compatibility means Code written years ago should continue to work in newer versions of Java. Old code must continue to work. This means old code like:

List list = new ArrayList();

must continue to work and newer code like:

List<Student> students = new ArrayList<>();

must also work. Both styles needed to coexist in the same application.

The Java team had to find a way to introduce Generics without breaking millions of existing applications.

Their solution was something called Type Erasure. Java needed a way to add Generics without breaking millions of existing applications.

What Is Type Erasure?

During compilation, Java checks:

List<Student> students = new ArrayList<>();

and ensures that only Student objects are added. Once compilation succeeds, Java removes the generic information.

At runtime:

List<Student>
List<Animal>
List<String>

all become:

List

The generic type exists only during compilation. That’s why it’s called Type Erasure. The type information gets erased.

Why Does RestTemplate Care?

Let’s go back to our original code:

List<FakeStoreProductDto> fakeStoreProductDtos =
        restTemplate.getForObject(
                url,
                List<FakeStoreProductDto>.class
        );

The API call happens at runtime. But at runtime Java no longer sees:

List<FakeStoreProductDto>

It only sees:

List

So Jackson receives a JSON response and asks:

“A List of what exactly?”

It has no answer. Because the generic information is gone. That’s why this approach fails.

The Trick: Use Arrays

Arrays are different. Unlike Generics, arrays preserve type information at runtime.

This works:

FakeStoreProductDto[] fakeStoreProductDtos =
        restTemplate.getForObject(
                "https://fakestoreapi.com/products/",
                FakeStoreProductDto[].class
        );

Even at runtime Java still knows:

FakeStoreProductDto[]

means:

Array of FakeStoreProductDto

Now Jackson knows exactly what object to create. Once we have the array, converting it to a List is easy:

List<Product> products = new ArrayList<>();
for (FakeStoreProductDto dto : fakeStoreProductDtos) {
    products.add(convertFakeStoreStoToProduct(dto));
}

Problem solved.

The Mental Model

Think of Generics as training wheels.

During compilation they help Java verify:

  • Correct types
  • No unsafe inserts
  • Compile-time safety

Once the application starts running, Java removes those training wheels.

At runtime:

List<Student>
List<String>
List<Product>

all become:

List

Arrays don’t have this problem because they retain their type information.

That’s why:

FakeStoreProductDto[].class

works, while

List<FakeStoreProductDto>.class

does not.

Final Takeaway

Type Erasure isn’t some advanced academic Java concept.

It’s something you’ll eventually encounter while working with frameworks like Spring Boot, Jackson, Hibernate, or RestTemplate.

The next time you see code like:

FakeStoreProductDto[].class

instead of:

List<FakeStoreProductDto>.class

you’ll know exactly why.

Because Generics disappear at runtime.

Arrays don’t.


메타데이터
post_id
a3fc75ecfd7b
slug
the-day-i-finally-understood-java-type-erasure-thanks-to-a-resttemplate-error-a3fc75ecfd7b
url
https://medium.com/@ashim.roy120388/the-day-i-finally-understood-java-type-erasure-thanks-to-a-resttemplate-error-a3fc75ecfd7b
canonical_url
https://medium.com/@ashim.roy120388/the-day-i-finally-understood-java-type-erasure-thanks-to-a-resttemplate-error-a3fc75ecfd7b
author_url
https://medium.com/@ashim.roy120388
status
ok
fetched_at
2026-06-24 11:06:28