Domanda

I've thought java erasure wipes generic types out in compile time however when i test it by myself i realized there are some information about generic types in Bytecode.

here is my test :

i wrote 2 classes:

import java.util.*;
public class Test {
    List integerList;
} 

and

import java.util.*;
public class Test {
    List<Integer> integerList;
} 

i compiled both classes and somewhere in generic class i saw this line

integerList{blah blah}Ljava/util/List;{blah blah}
Signature{blah blah}%Ljava/util/List<Ljava/lang/Integer;>;{blah blah}<init>

in non generic class :

integerList{blah blah}Ljava/util/List;{blah blah}<init>

so obviously i have generic information inside bytecode so what is this erasure thing ??

È stato utile?

Soluzione 2

Some Generic type information is stored in Signature attributes . Refer JLS 4.8 and 4.6 and JVM spec 4.3.4. Read here:

Probably the most common complaint about generics in Java is that they are not reified - there is not a way to know at runtime that a List<String> is any different from a List<Long>. I've gotten so used to this that I was quite surprised to run across Neil Gafter's work on Super Type Tokens. It turns out that while the JVM will not track the actual type arguments for instances of a generic class, it does track the actual type arguments for subclasses of generic classes. In other words, while a new ArrayList<String>() is really just a new ArrayList() at runtime, if a class extends ArrayList<String>, then the JVM knows that String is the actual type argument for List's type parameter.

and Neal Gafter's blog.

Altri suggerimenti

what is this erasure thing ??

Erasure is a mapping from generic to raw types. The common phrase "because of erasure" is essentially meaningless. What is significant are the specifications that use the mapping.

There are two interesting uses.

  • It's used to map method signatures from using generics to raw types. It is the raw-type signatures that used for overloading. This causes the vast majority of the problems with "erasure". For instance, you can't have two methods add(List<String>) and add(List<Integer>) in the same type. Overloading probably isn't a great idea, and there's not a great willingness to add this feature.

  • The type available for an instance of an object at runtime is the type it was created with erased. So if you cast to, say, (String) that will be checked at runtime, but if you cast to List<String> only the erasure of that type (List) will be checked. You can have the variables of type List<String> and List<Integer> point to exactly the same instance. In practice, you shouldn't be using casts (of reference types) in 1.5 and later.

Where practical, generic information is kept in class files and made available through reflection. So you'll find it on class definitions, supertypes, fields, methods, constructors, etc.

This is one instance where accurate use of terminology actually matters: Bytecode is the instruction set of the Java Virtual Machine. A class file contains bytecode, but also information used for linking (field signatures, method signatures, ...), for the bytecode verifier, for the debugger, ...

Type erasure means that generic type informations is not translated into byte code; more specifically, all instances of a generic type share the same representation in byte code. Likewise, the dynamic type of an object the runtime keeps track of (as used by the cast and instanceof operators, and available through getClass()) is the same for all instances of a generic class, irrespective of any type parameters supplied in the source code.

Your experiment proves that generic type information is retained in the class file, more specifically, in the types of method and field signatures. That's unsurprising, because the signatures are actually used at compile time. The might also be used at link time, and are even accessible through the reflection api. The crucial difference is that they are the declared types of fields or methods, not the runtime types of actual objects.

That is, since Java 1.5 we must distinguish between a variable's declared type, and the runtime type of the object it refers to. The former supports generics, the latter does not. And yes, this means there isn't a one-to-one correspondence between compile time and runtime types.

Type info will be erased from here

 integerList = new ArrayList<Integer>(); 

in the bytecode it will be equivalent to

integerList = new ArrayList(); 

and there is no chance to know in runtime from integerList object what was its compile time type.

Erasure means that generic typing is not incorporated in the byte code (when the list is created or used).

The signature you see is used just to indicate that the field is generic.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top