8

Consider this example taken from a book, with a super class Gen and a subclass Gen2...

class Gen<T> { }

class Gen2<T> extends Gen<T> { }

Now the book states following will not compile (lets assume its in a main method)

Gen2<Integer> obj = new Gen2<Integer>();

if (obj instanceof Gen2<Integer>) {

    //do something
}

This can't be compiled because generic type info does not exist at runtime. If it doesn't exist at runtime, when does it exist? I thought that it would not exist at compile time, but would exist at runtime. Certainly, the following works for runtime with a wildcard...

if (obj instanceof Gen<?>) {

    //do something else

}

So to clarify, my question is why does generic type info not exist at runtime? Have I overlooked a simple concept?

Judge Mental
  • 5,091
  • 15
  • 20
Rob L
  • 2,650
  • 3
  • 25
  • 47
  • 8
    Backwards compatibility. – Sotirios Delimanolis Mar 17 '14 at 23:15
  • possible duplicate of [What exactly is meant by backwards compatibility in case of Java Generics?](http://stackoverflow.com/questions/22457900/what-exactly-is-meant-by-backwards-compatibility-in-case-of-java-generics) – Exupery Mar 17 '14 at 23:18
  • 1
    Perhaps what you have overlooked is [erasure](http://docs.oracle.com/javase/tutorial/java/generics/erasure.html). This is just the way generics are implemented. – Radiodef Mar 17 '14 at 23:18
  • @Exupery - Had I known what it was called I would have done more research before asking... my apologies. – Rob L Mar 17 '14 at 23:19
  • @Radiodef - ok that is the next chapter in the book I am reading. Maybe I was too quick to want to figure it out. Thank you. – Rob L Mar 17 '14 at 23:21
  • 3
    After all the answers about backwards compatibility, here are other reasons for type erasure: http://stackoverflow.com/questions/20918650/are-there-benefits-to-javas-type-erasure – herman Mar 17 '14 at 23:59

3 Answers3

10

The problem is that generics was not always present in java (I think they added it in 1.5). So in order to be able to achieve backwards compatibility there is type erasure which effectively erases generic type information while compiling your code in order to achieve that goal.

Excerpt from the relevant parts of the official documentation:

During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded.

So this code for example

public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

becomes this after type erasure:

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}

There is a way however to resurrect some of that type information if you tread the path of reflection which is like a lightsaber: powerful but also dangerous.

Adam Arold
  • 26,256
  • 18
  • 92
  • 176
  • 1
    This is a well thought out helpful answer. I am going to read about type erasure right now, but this definitely gives me a better understanding about Java generics and type erasure. Thank you! – Rob L Mar 17 '14 at 23:33
  • 1
    The official Oracle docs are rather thorough and understandable. Consider reading thorugh the generics *trail* on their site. – Adam Arold Mar 17 '14 at 23:33
8

You have "overlooked a simple concept". The generics exist only at compile time, and only to enforce things like parametric polymorphism. Because the folks who implemented them decided that developers should be able to use them (generics) and still deploy their built artifacts to targets with older JVMs (an extremely questionable decision in my mind, as the runtime libraries also changed between 1.4 and 1.5), they had the compiler decide if everything type-checks and then they throw away almost all of that information before creating the compiled artifact.

I say almost all of that information because in some special cases it is still around at runtime.

nyanpasu64
  • 1,970
  • 1
  • 16
  • 27
Judge Mental
  • 5,091
  • 15
  • 20
  • Ok, that makes sense and that's the backwards compatibility someone else had mentioned. Thanks for clarifying. – Rob L Mar 17 '14 at 23:25
  • 1
    What the check were they thinking?? How do you write any of your own code that uses , ? I'm trying to implement a GenericCacheAside implementation, something I did in C# easily.....I hit this issue. Boo. Error message = "The run time type is not available, got: TEntity" – granadaCoder Sep 26 '18 at 23:21
0

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. [...] Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

I don't think this is related to backwards compatibility at all. If you compile your code targetting 1.5 or more, the code will be not compatible with 1.4, regardless the use of generics.

Giuseppe Bertone
  • 1,842
  • 11
  • 13
  • How does that explain why `obj instanceof Gen2` does not compile while `obj instanceof Gen2>` does? – Sotirios Delimanolis Mar 17 '14 at 23:30
  • Because generic information is not present at runtime, only at compile time. The compiler knows that the VM will not be able to know the type of the object, so it does not compile even if you want to use the 1.5 compiler. `Gen2>` is a wildcard, so it means every object is ok, so it compiles. – Giuseppe Bertone Mar 17 '14 at 23:36
  • `Gen2>` is very much different than `Get2`. Also, not all generic information is lost at run time. – Sotirios Delimanolis Mar 17 '14 at 23:37
  • Every class extends Object, so the effect is the same. But because Object is a type itself, the compiler "_cannot perform instanceof check against parameterized type Gen2. Use the form Gen2> instead since further generic type information will be erased at runtime_". It seems to me simply a syntax related issue, not really a backward compatibility issue. And, what are the generic information retained at runtime? – Giuseppe Bertone Mar 17 '14 at 23:44
  • @Neurone It is (was) a compatibility issue with the existing Java *types* and byte-code. When .NET introduced generics in 2.0 they chose a different approach (with "reified types"), but it also means `List` and `List` are two *different* types and moving from .NET 2.0 from .NET 1.1 was a *non-backwards compatible change* (even though the ancient C#1 *source* can be compiled for .NET 2). – user2864740 Mar 18 '14 at 00:01