Google Guice + generics: Is there some magic behind the curtains? - java

It seems to me that Guice implementation is doing some very tricky things when dealing with generics. It looks like it knows at runtime about generic types used at compile time. Let's see a simple example:
#Inject
public void Bar(Provider<Foo> genericInjector){
...
At runtime Guice will inject a correct implementation of Provider here (i.e. the one that provides Foo instances). But from what I know, generic types are erased at runtime (see: Type Erasure ). So all that Guice really sees at runtime is:
#Inject
public void Bar(Provider genericInjector){
....
So how is it possible that Guice knows which implementation of Provider to inject?

No, type erasure doesn't erase everything. You can still get the types of fields, parameters etc. The Provider<Foo> information is still present at execution time. See Method.getGenericParameterTypes for example.
What isn't preserved is the type information about specific objects. For example, if I write:
List<String> list = new ArrayList<String>();
showType(list);
...
public static void showType(List<?> list)
{
// ???
}
There's no way that can work out that it's an ArrayList<String> because the object doesn't have that information any more.
See the Java Generics FAQ for a lot more information.

It is a common misconception that the way type erasure works is that the compiler essentially removes angle brackets and what's inside them and then acts as if the source was Java 1.4. This is not the case.
Generic parameters don't get erased from method signatures – method signatures are "compile-time". Generic parameters don't exist in "runtime" constructs – you can't tell what type parameters a given object was instantiated with.

Related

does Java type erasure erase my generic type?

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

Why I'm not required to specify a type argument when using a Generic class?

I was surprised today when this code compiled:
class GenericClass<T> {
public void emptyMethod(T instance) {
// ..
}
public void print(T instance) {
System.out.println(instance);
}
}
public class Main {
public static void main(String[] args) {
GenericClass first = new GenericClass();
System.out.println("Wow");
first.emptyMethod(10);
first.print(16);
}
}
The compiler emits a warning (Type safety: The method emptyMethod(Object) belongs to the raw type GenericList. References to generic type GenericList should be parameterized), but anyway it does not cause a compiler error and it runs 'fine' (at least the provided print method). As I'm understanding, the compiler is using object as the type argument, but I find it counter-intuitive. Why would the compiler do such thing? Why it doesn't require me to specify the type parameter?
You're using a raw class, basically.
Think back to when generics were first introduced in Java: there was a load of code which already used List, ArrayList etc. In order to avoid breaking all of that code, but still reusing the existing classes, raw types were introduced - it's basically using a generic type as if it weren't one.
As you can see, you get a warning - so it's worth avoiding - but that's the primary reason for it being allowed at all.
See section 4.8 of the JLS for more information, which includes:
Raw types are closely related to wildcards. Both are based on existential types. Raw types can be thought of as wildcards whose type rules are deliberately unsound, to accommodate interaction with legacy code. Historically, raw types preceded wildcards; they were first introduced in GJ, and described in the paper Making the future safe for the past: Adding Genericity to the Java Programming Language by Gilad Bracha, Martin Odersky, David Stoutamire, and Philip Wadler, in Proceedings of the ACM Conference on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA 98), October 1998.
You have to know how generics are implemented in Java. They are far from perfect.
You have to remember that during run time everything is an Object. There are no types during run time.
Generics were added for added security in places, where you need it, but if you don't want to use it, you can ignore warnings and use unparametrized instances.
However, if you'd like java compiler to help you with type safety, then you parametrize generic class instances. Once you create a GenericClass for example, compiler will not allow you to use it with an integer parameter (first.emptyMethod(10) will not compile). You can still make it work with integer parameter if you do explicit type casting though.
So consider it a good practice for added security, which only works if you follow the rules.

Proper use of Java Generics

I have the following function:
/**
* Finds all entities of a certain type
* #param <T> The type of the entity
* #param entityType The class of the entity
* #return A list of all the entities found, null if the entity is not in the database
* or on error
*/
public <T> List<T> findAll(Class entityType)
{
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityType));
return getEntityManager().createQuery(cq).getResultList();
}
You will note it is rather repetitive. Is there anyway that I can refactor this function so that it does not need to take a Class as a parameter. Is there anyway that I can use the generic type being passed in?
No, you cannot — directly. In a couple paragraphs I'll show you another way around the problem.
Java generics are implemented via type erasure, meaning that all type information is stripped away at runtime. When your method is invoked, it knows that it is supposed to return a List, but at run time there is nothing to tell it that it is supposed to return List<Foo>. The ArrayList constructor doesn't need access to the class object to do its job; your code, however, does.
The way around this is to just pass the class which, since this is a generic method (as opposed to a generic class), you can do. If you change the declaration of your method to be
public <T> List<T> findAll(Class<T> entityType)
Then you can call it with the class:
findAll(String.class)
and the compiler will automatically detect that it is supposed to return a List<String>. It reduces the redundancy, but by inferring the type argument from the class rather than the other way around. This is the standard way to solve this kind of problem — it shows up a lot in libraries like Guava.
The standard practice to create a "Generic DAO" is to have an abstract class that is parametrizable and have subclasses with a specific parameter. This way, the method itself is already parametrized with the correct type.
Take a look at this for an example:
http://netbeans.org/projects/samples/sources/samples-source-code/content/samples/javaee/AffableBean/src/java/session/AbstractFacade.java
Not really, no. Generics are a compile time feature. At run time a caller of your API can supply any instance of Class<T> for entityType so you need it available at runtime to provide to hibernate. The compiler has no ability to basically build a separate version of the method for every possible T, which is what it would have to do in order to omit the class parameter.
Also, Class is a raw type, you are already using generics improperly ;)
Java uses Type erasure, which means that the generic type parameter is not available at runtime (used at compile time to ensure your code is correct wrt to types). This means that to use persistence you will need to explicitly pass the class so the runtime can figure out how to do the persistence (e.g. what class the persisted object belongs to)
The simple answer is "no". Due to something called type erasure, all parameterized types are treated as Object.class at runtime in the compiled bytecode. The generic types are only used at compile time to prevent you from using things wrong.
You can more or less do what you're asking in Scala. (I'm not sure if you can use scala, but just for reference, here it is).
object Main extends App{
def findAll[T : Manifest]() : Array[T] = {
var a = new Array[T](0)
println(a.getClass)
a
}
var result = findAll[String]()
println(result.getClass)
var result2 = findAll[Array[Int]]()
println(result2.getClass)
}
By using the implicit Manfiest, the scala compiler keeps a record of what generics get erased on compile.

Java Generics: Accessing Generic Type at runtime

I'm looking to access the generic type of a declared field during runtime. I was previously under the impression that this was not possible due to the Java type erasure. However, this must not be the case because some well known frameworks leverage the generic type through reflection during runtime.
As an example, Guice will implement a Provider based upon the generic type you provide:
public class Injectable{
#Inject
private Provider<SomeType> someTypeProvider;
}
How does one access the 'SomeType' generic attribute of a field or any such type/method/etc through the reflection API?
Additionally it would be helpful to also know how to access these generic type attributes through the Java 6 Annotation Processor API.
Thanks.
Edit:
Thank you all for your great pointers. I found a way to do this using haylem's links, specifically the one to Prenkov's article Java Reflection: Generics.
Here's the answer I was looking for:
/**
* #author John Ericksen
*/
public class TypeReflectionExample {
public class SomeType{}
public class Injectable{
#Inject private Provider<SomeType> someTypeProvider;
}
public static void main(String[] args){
try {
Field providerField = Injectable.class.getDeclaredField("someTypeProvider");
Type genericFieldType = providerField.getGenericType();
if(genericFieldType instanceof ParameterizedType){
ParameterizedType aType = (ParameterizedType) genericFieldType;
Type[] fieldArgTypes = aType.getActualTypeArguments();
for(Type fieldArgType : fieldArgTypes){
Class fieldArgClass = (Class) fieldArgType;
System.out.println("fieldArgClass = " + fieldArgClass);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
results in:
fieldArgClass = class test.TypeReflectionExample$SomeType
The same can be done for Methods, Constructors, Superclass extensions/implements, etc
I'm awarding haylem, as his post led me to this solution, even if it didn't directly answer my question.
It is true that generics aren't generally known at runtime in Java, because they are implemented with Type Erasure.
Reflecting Generics?
However, you can stil extract some valuable information about the declared types (NOT the runtime objects' types), as presented in Ian Roberston's article Reflecting Generics and Prenkov's article Java Reflection: Generics.
Background on Generics and Type Erasure
Generics where introduced while conserving backwards compatibility at the source qnd binary level, hence some of their limitation, like:
the impossibility to have a short-hand form without at least some indicator for generics support (here, the so-called diamond operator <>),
the impossibility to inspect generic-types at runtime, because they had to be implemented with Type Erasure.
Further Reading
From The Java Tutorial:
section on Generic Types
section on Type Inference and Instantiation of Generic Classes
From the Java Language Specifications (JLS):
Java SE 5's JLS section on Types, Values and Variables
Java SE 7's JLS section on Types, Values and Variables
From good StackOverflow questions:
Java Raw Type and generics interaction
Others:
IBM Developer Series: Java Theory and Practice: Generics Gotchas (especially the sections The Road Not Taken, Generifying Existing Classes and Implications of Erasure).
Well, why don't you look at what guice does? The source is publicly available.
Guice does these things at multiple levels. One that particular sticks out are type literals.
The key point here is that while types are compiled using type erasure (so there is only one class for each type), there still exist multiple Type objects, that do know the generics used. However, the code is optimized independently of that (as it was compiled per class, and not per type).
Have a look at the Java API of ParameterizedType.
So while it is correct that Java Generics are implemented by "type erasure" on a class level, this doesn't completely hold on a Type level. Unfortunately, handling Types is much more tricky than classes. Plus, this also implies that Java cannot optimize generics in the same way that C++ does, in particular for primitive types. An ArrayList<Integer> will by design be an ArrayList<?> containing objects, and not backed by a native int[] array when possible.
Note that this is, however, rather close to keeping track of these things yourself. Say, very naively (it will not work with nested generics), you could just extend ArrayList<T> with a class, that has a field Class<T> contentClass, then you will be able to find out this information at runtime. (A TypeLiteral may be the better choice instead of a Class here, though!) Plus, the JRE will actually not ensure that the list remains consistent. Just like you could cast a ArrayList<Integer> into an untyped ArrayList and add a String object.
I've used this a year or so ago. It could help you out
Type typeOfSrc = type(YourClass.class, clazz);
// type(C, A1,...,An) => C<A1,...,An>
static ParameterizedType type(final Class raw, final Type... args)
{
return new ParameterizedType()
{
public Type getRawType(){ return raw; }
public Type[] getActualTypeArguments(){ return args; }
public Type getOwnerType(){ return null; }
};
}
This allows you to access the Generic type at run time. Basically what you're doing here is storing the Generic information in another class, and using that class for retrieving that information at run time.
I believe guice uses TypeLiteral to encapsulate generic information in a separate object.

Generating methods with generic types with Asm bytecode generator (ClassWriter)

Defining simple getters and setters is easy using Asm (and fortunately it is even explained in their FAQ). But one thing that is not mentioned, and for which I have been unable to find documentation, is how to implement these using generic type information.
I am actually able to determine generic type information itself quite easily (since code will take existing fields and/or methods and full generic type handling and resolution exists). I just need to generate generics version for types that have generic type included.
I hope this is something as easy as modifying signature Asm ClassWriter/MethodVisitor calls take, but some comments in documentation indicate it might not be that easy (as generics information is stored in bit different place than regular info).
EDIT: looks like entry point is "ClassWriter.visitField/Method(...., String signature) -- note that it's "description" that contains normal non-generic class information, but term "signature" (in JLS) specifically refers to generics-including type information.
You can build the signature using ASM's SignatureWriter class.
For example, suppose you wish to write the signature for this method:
public <K> void doSomething(K thing)
You could use this code:
SignatureWriter signature = new SignatureWriter();
signature.visitFormalTypeParameter("K");
// Ensure that <K> extends java.lang.Object
{
SignatureVisitor classBound = signature.visitClassBound();
classBound.visitClassType(Type.getInternalName(Object.class));
classBound.visitEnd();
}
// The parameter uses the <K> type variable
signature.visitParameterType().visitTypeVariable("K");
// The return type uses the void primitive ('V')
signature.visitReturnType().visitBaseType('V');
signature.visitEnd();
String signatureString = signature.toString();
Which is equivalent to:
String signatureString = "<K:Ljava/lang/Object;>(TK;)V;"
In my experience most on-the-fly bytecode generation libraries don't have good support for generic types; however erased classes work just fine (unless you want to introspect those classes later, of course).

Categories