11

In this tutorial on reflection it states:

[...] because generics are implemented via type erasure which removes all information regarding generic types during compilation

My knowledge was that generics are used so that at compile time the compiler can check for type safety. i.e fail fast approach. But the link mentions that type erasure removes generic information during compilation.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Victor
  • 16,609
  • 71
  • 229
  • 409
  • 3
    During compilation but after validation. – Sotirios Delimanolis Oct 08 '13 at 16:24
  • 2
    Obviously the information isn't removed before the compiler is done using the information. – Dave Newton Oct 08 '13 at 16:26
  • The compiler doesn't remove anything from your `.java` file instead it creates a `.class` file and it doesn't copy comments, imports, or all the generic information (some is still there) Since you start with nothing, you can't be removing them. – Peter Lawrey Oct 08 '13 at 16:49

4 Answers4

14

The statement that you quoted is correct: the compiler uses the generic type information internally during the process of compilation, generating type-related errors as it processes the sources. Then, once the validation is done, the compiler generates type-erased byte code, with all references to generic types replaced with their respective type erasure.

This fact becomes evident when you look at the types through reflection: all interfaces, classes, and functions become non-generic, with all types tied to generic type parameters replaced with a non-generic type based on the generic type constraints specified in the source code. Although reflection API does have provisions for accessing some of the information related to generics* at runtime, the virtual machine is unable to check the exact generic type for compatibility when you access your classes through reflection.

For example, if you make a class member of type List<String> and try setting a List<Integer> into it, the compiler is going to complain. If you try to do the same through reflection, however, the compiler is not going to find out, and the code will fail at run-time in the same way that it would without generics:

class Test {
    private List<String> myList;
    public void setList(List<String> list) {
        myList = list;
    }
    public void showLengths() {
        for (String s : myList) {
             System.out.println(s.length());
        }
    }
}

...

List<Integer> doesNotWork = new ArrayList<Integer>();
doesNotWork.add(1);
doesNotWork.add(2);
doesNotWork.add(3);
Test tst = new Test();
tst.setList(doesNotWork); // <<== Will not compile
Method setList = Test.class.getMethod("setList", List.class);
setList.invoke(tst, doesNotWork); // <<== This will work;
tst.showLengths(); // <<== However, this will produce a class cast exception

Demo on ideone.


* See this answer for details on getting information related to generic types at runtime.

Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • *generic types replaced with `java.lang.Object`, their respective type erasure*. – Rohit Jain Oct 08 '13 at 16:28
  • @RohitJain You're right, it's not always `java.lang.Object` - thanks! – Sergey Kalinichenko Oct 08 '13 at 16:34
  • 1
    This is the reason why it is called "erasure types". – Mauren Oct 08 '13 at 17:12
  • 3
    -1; this explanation is misleading. You _can_ get some of the generic type constraints back by using reflection. For example, if you call `Test.class.getDeclaredField("myList").getGenericType()`, you will get `List`, not `List`. [Demo on ideone](http://ideone.com/y6wPHG). – Louis Wasserman Oct 08 '13 at 22:17
  • @LouisWasserman Thanks for the comment. Please take a look at the edit, and let me know if you think that the explanation remains misleading even after the edit. Thanks! – Sergey Kalinichenko Oct 09 '13 at 17:23
  • I'm still not quite satisfied. It's...tricky. My understanding of erasure is that almost all generics are lost in the _implementation_ of methods and constructors and the like, and almost all generics are kept in the "interface": classes, fields, method signatures, etc. Method overloads must be unique modulo type erasure, but their types aren't actually erased. – Louis Wasserman Oct 09 '13 at 18:21
  • @LouisWasserman That is what I was trying to say in the second sentence of the second paragraph. If it does not sound clear, would you mind suggesting an edit? Thanks again. – Sergey Kalinichenko Oct 09 '13 at 18:32
  • My main concern is that "all interfaces, classes, and functions become non-generic" is pretty much the opposite of what happens: interfaces, classes, and functions are the things that _stay_ generic. – Louis Wasserman Oct 09 '13 at 19:17
  • @LouisWasserman But it's not interfaces, classes, and functions that stay generic, it's only their *metadata* that does. The actual interfaces, classes, and functions (i.e. the implementations) become type-erased, keeping the metadata for reference, but unable to act on it. The metadata "remembers" that something takes a list of strings, but it has no way of complaining when someone sticks a list of objects of some other type. – Sergey Kalinichenko Oct 09 '13 at 19:30
  • @dasblinkenlight I very much agree with @LouisWasserman here, erasure happens at a particular *call-site*, not in the types themselves. if you compile this method `public static void fun(List list) { }` and than would look at the byte-code, you would see `public static void fun(java.util.List);`, but *at the call-site* it would become erased: `invokestatic ... Method fun:(Ljava/util/List;)V` – Eugene Aug 30 '18 at 20:15
4

Some generics stay in the compiled class -- specifically including method signatures and class definitions, for example. At runtime, no objects keep their full generic type, but even at runtime you can look up the generic definition of a class or a method.

For example, if you have

class Foo {
  List<String> getList() { ... }

  public static void main(String[] args) {
    System.out.println(Foo.class.getMethod("getList").getGenericReturnType());
    // prints "List<String>"
    List<String> list = new Foo().getList();
    // there is no way to get the "String" parameter on list
}
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
1

It means, when it is being converted into bytecode. For checking whether correct types are used or not, generics are used. But at the time of bytecode generation, information is removed

Prasad Kharkar
  • 13,410
  • 5
  • 37
  • 56
1

In java, Generics is just a place holder. Java Run Time doesn't have any clue about the generics. It's all a compile time trick.

Setting up generics is exactly similar to the scenario, when you declare a field attribute in a class as

  • Object (When you just declare type as T)
  • MyObject (When you declare type as T extends MyObject).

After compilation, all would be wired according to the types. That's what is known as Type Erasure.

Supun Wijerathne
  • 11,964
  • 10
  • 61
  • 87