Take the 2-minute tour ×

What is the concept of erasure in generics in java?

share|improve this question
add comment

6 Answers 6

up vote 74 down vote accepted

It's basically the way that generics are implemented in Java via compiler trickery. The compiled generic code actually just uses java.lang.Object wherever you talk about T (or some other type parameter) - and there's some metadata to tell the compiler that it really is a generic type.

When you compile some code against a generic type or method, the compiler works out what you really mean (i.e. what the type argument for T is) and verifies at compile time that you're doing the right thing, but the emitted code again just talks in terms of java.lang.Object - the compiler generates extra casts where necessary. At execution time, a List<String> and a List<Date> are exactly the same; the extra type information has been erased by the compiler.

Compare this with, say, C#, where the information is retained at execution time, allowing code to contain expressions such as typeof(T) which is the equivalent to T.class - except that the latter is invalid. (There are further differences between .NET generics and Java generics, mind you.) Type erasure is the source of many of the "odd" warning/error messages when dealing with Java generics.

Other resources:

share|improve this answer
@Rogerio: No, the objects won't have different generic types. The fields know the types, but the objects don't. –  Jon Skeet Dec 28 '09 at 8:55
@Rogerio: Absolutely - it's extremely easy to find out at execution time whether something which is only provided as Object (in a weakly typed scenario) is actually a List<String>) for example. In Java that's just not feasible - you can find out that it's an ArrayList, but not what the original generic type was. This sort of thing can come up in serialization/deserialization situations, for example. Another example is where a container has to be able to construct instances of its generic type - you have to pass that type in separately in Java (as Class<T>). –  Jon Skeet Dec 29 '09 at 13:15
I never claimed it was always or almost always an issue - but it's at least reasonably frequently an issue in my experience. There are various places where I'm forced to add a Class<T> parameter to a constructor (or generic method) simply because Java doesn't retain that information. Look at EnumSet.allOf for example - the generic type argument to the method should be enough; why do I need to specify a "normal" argument as well? Answer: type erasure. This sort of thing pollutes an API. Out of interest, have you used .NET generics much? (continued) –  Jon Skeet Dec 29 '09 at 13:18
Before I used .NET generics, I found Java generics awkward in various ways (and wildcarding is still a headache, although the "caller-specified" form of variance definitely has advantages) - but it was only after I'd used .NET generics for a while that I saw how many patterns became awkward or impossible with Java generics. It's the Blub paradox again. I'm not saying that .NET generics doesn't have downsides either, btw - there are various type relationships which can't be expressed, unfortunately - but I far prefer it to Java generics. –  Jon Skeet Dec 29 '09 at 13:21
@Rogerio: There's a lot you can do with reflection - but I don't tend to find I want to do those things nearly as often as the things that I can't do with Java generics. I don't want to find out the type argument for a field nearly as often as I want to find out the type argument of an actual object. –  Jon Skeet Dec 30 '09 at 7:54
show 12 more comments

To complete the already very complete Jon Skeet's answer, you have to realize the concept of type erasure derives from a need of compatibility with previous versions of java.

Initially presented at EclipseCon 2007 (no longer available), the compatibility included those points:

  • Source compatibility (Nice to have...)
  • Binary compatibility (Must have!)
  • Migration compatibility
    • Existing programs must continue to work
    • Existing libraries must be able to use generic types
    • Must have!

Original answer:


new ArrayList<String>() => new ArrayList()

There are propositions for a greater reification. Reify being "Regard an abstract concept as real", where Language constructs should be concepts, not just syntactic sugar.

I should also mention the checkCollection method of Java6, which returns a dynamically typesafe view of the specified collection. Any attempt to insert an element of the wrong type will result in an immediate ClassCastException.

The generics mechanism in the language provides compile-time (static) type checking, but it is possible to defeat this mechanism with unchecked casts.

Usually this is not a problem, as the compiler issues warnings on all such unchecked operations.

There are, however, times when static type checking alone is not sufficient, like:

  • when a collection is passed to a third-party library and it is imperative that the library code not corrupt the collection by inserting an element of the wrong type.
  • a program fails with a ClassCastException, indicating that an incorrectly typed element was put into a parameterized collection. Unfortunately, the exception can occur at any time after the erroneous element is inserted, so it typically provides little or no information as to the real source of the problem.

Update July 2012, almost 4 years later:

It is now (2012) detailed in "API Migration Compatibility Rules (Signature Test)"

The Java programming language implements generics using erasure, which ensures that legacy and generic versions usually generate identical class files, except for some auxiliary information about types.
Binary compatibility is not broken because it is possible to replace a legacy class file with a generic class file without changing or recompiling any client code.

To facilitate interfacing with non-generic legacy code, it is also possible to use the erasure of a parameterized type as a type.
Such a type is called a raw type (Java Language Specification 3/4.8).
Allowing the raw type also ensures backward compatibility for source code.

According to this, the following versions of the java.util.Iterator class are both binary and source code backward compatible:

Class java.util.Iterator as it is defined in Java SE version 1.4:

public interface Iterator {
    boolean hasNext();
    Object next();
    void remove();

Class java.util.Iterator as it is defined in Java SE version 5.0:

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
share|improve this answer
i love how this implies Jon Skeet is complete, and not his answer =P. might have been intentional? –  Claudiu Nov 24 '08 at 7:48
Note that backwards compatibility could have been achieved without type erasure, but not without Java programmers learning a new set of collections. That's exactly the route that .NET went. In other words, it's this third bullet which is the important one. (Continued.) –  Jon Skeet Nov 24 '08 at 7:55
Personally I think this was a myopic mistake - it gave a short term advantage and a long term disadvantage. –  Jon Skeet Nov 24 '08 at 7:55
@Claudiu: I must have read Jon Skeet facts one time too many ;) stackoverflow.com/questions/305223/jon-skeet-facts –  VonC Nov 24 '08 at 8:13
@VonC the presentation link doesn't work. –  Inquisitive Jul 24 '12 at 3:09
show 1 more comment

Erasure, literally means that the type information which is present in the source code is erased from the compiled bytecode. Let us understand this with some code.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericsErasure {
    public static void main(String args[]) {
        Iterator<String> iter = list.iterator();
        while(iter.hasNext()) {
            String s = iter.next();

If you compile this code and then decompile it with a decompiler such as JAD , you will get something like this. Notice that the decompiled code contains no trace of the type information present in the original source code.

import java.io.PrintStream;
import java.util.*;

public class GenericsErasure

    public GenericsErasure()

    public static void main(String args[])
        List list = new ArrayList();
        String s;
        for(Iterator iter = list.iterator(); iter.hasNext(); System.out.println(s))
            s = (String)iter.next();

share|improve this answer
My dear link was broken update it :) +1 good answer :) –  sunny Oct 8 '13 at 6:48
add comment

Just as a side-note, it is an interesting exercise to actually see what the compiler is doing when it performs erasure -- makes the whole concept a little easier to grasp. There is a special flag you can pass the compiler to output java files that have had the generics erased and casts inserted. An example:

javac -XD-printflat -d output_dir SomeFile.java

The -printflat is the flag that gets handed off to the compiler that generates the files. (The -XD part is what tells javac to hand it to the executable jar that actually does the compiling rather than just javac, but I digress...) The -d output_dir is necessary because the compiler needs some place to put the new .java files.

This, of course, does more than just erasure; all of the automatic stuff the compiler does gets done here. For example, default constructors are also inserted, the new foreach-style for loops are expanded to regular for loops, etc. It is nice to see the little things that are happening automagically.

share|improve this answer
add comment

Complementing the already-complemented Jon Skeet answer...

It has been mentioned that implementing generics through erasure leads to some annoying limitations (e.g. no new T[42]). It has also been mentioned that the primary reason for doing things this way was backwards compatibility in the bytecode. This is also (mostly) true. The bytecode generated -target 1.5 is somewhat different from just de-sugared casting -target 1.4. Technically, it's even possible (through immense trickery) to gain access to generic type instantiations at runtime, proving that there really is something in the bytecode.

The more interesting point (which has not been raised) is that implementing generics using erasure offers quite a bit more flexibility in what the high-level type system can accomplish. A good example of this would be Scala's JVM implementation vs CLR. On the JVM, it is possible to implement higher-kinds directly due to the fact that the JVM itself imposes no restrictions on generic types (since these "types" are effectively absent). This contrasts with the CLR, which has runtime knowledge of parameter instantiations. Because of this, the CLR itself must have some concept of how generics should be used, nullifying attempts to extend the system with unanticipated rules. As a result, Scala's higher-kinds on the CLR are implemented using a weird form of erasure emulated within the compiler itself, making them not-entirely-compatible with plain-old .NET generics.

Erasure may be inconvenient when you want to do naughty things at runtime, but it does offer the most flexibility to the compiler writers. I'm guessing that's part of why it's not going away any time soon.

share|improve this answer
The inconvenience isn't when you want to do "naughty" things at execution time. It's when you want to do perfectly reasonable things at execution time. In fact, type erasure allows you to do far naughtier things - such as casting a List<String> to List and then to List<Date> with only warnings. –  Jon Skeet Nov 24 '08 at 9:13
add comment

As I understand it (being a .net guy) the jvm has no concept of generics so the compiler replaces type parameters with Object and performs all the casting for you.

This means that java generics are nothing but syntax sugar and don't offer any performance improvement for value types that require boxing/unboxing when passed by reference.

share|improve this answer
Java generics can't represent value types anyway - there's no such thing as a List<int>. However, there's no pass-by-reference in Java at all - it's strictly pass by value (where that value may be a reference.) –  Jon Skeet Nov 24 '08 at 7:31
OK :] Thanks for correcting me. –  Andrew Kennan Nov 24 '08 at 7:35
add comment

Your Answer


