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.
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
TwithStudent. 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