Can I use the class field on an ArrayList<T> instance in Java?
I have this code, which compiles:
new TypeToken<ArrayList<ServerTask>>() {}.getType()
Then I have tried
ArrayList<ServerTask>.class
which does not compile.
I am new to Java programming (came from C#) and I have thought that T.class
i开发者_高级运维s an exact equivalent of typeof(T)
in C#. Apparently there is something really basic that I do not understand, so my question is what is wrong with ArrayList<ServerTask>.class
and do I have no choice, but use new TypeToken<ArrayList<ServerTask>>() {}.getType()
instead? Is there a shorter (nicer, saner) form?
Thanks.
Unfortunately (?) Java implements generics using Type Erasure.
This means that there is no construct to get the generic type, and at runtime your ArrayList actually only stores Objects.
To be exact, Generics in Java are a compile-time only construct. They guarantee type-safety at compile time only.
EDIT: I just noticed this bit of code -
new TypeToken<ArrayList<ServerTask>>() {}.getType()
This is a workaround for the limitations of type erasure. Type information is preserved if you extend a generic class with a specific type. For example:
public interface List<T> {
}
public class StringList implements List<String> {
// The bytecode of this class contains type information.
}
In your example, you are trivially extending the class TypeToken
, causing the compiler to generate an anonymous inner class that contains the type information. The implementation of TypeToken.getType()
will use reflection to give you this type information.
EDIT2: For a more detailed explanation, have a look at Reflecting Generics.
Your first code sample creates an anonymous subclass of TypeToken complete with concrete generic bindings. The type returned will be an instance of Class
. But beware, because
(new TypeToken<ArrayList<ServerTask>>() {}.getType()).getClass(TypeToken.class)
will return false!
In your second code sample, you're trying to do something that Java doesn't support because of how generics are implemented with type erasure. There is no class that represents an ArrayList
whose generic parameter is bound...from the compiler's point of view an ArrayList
is an ArrayList
regardless of the generic type, so the expression doesn't compile.
It's possible that neither piece of code is going to work out quite right. If you need to be playing around classes with generic parameters, you may want to look into gentyref, which I've found to be very helpful in simplifying the API to ask the kinds of questions you're trying to get answers to.
About the ".class" construct, it is not an operator nor an instance member, it is actually a way to tell the language "go and find the Class object for this class name before the dot". Is a way of constructing class literals.
As specified in the JLE, section 15.8.2, the compiler will complain if you try to use it with a parameterized type (among others).
The example given by Bringer128 is considered an anti-pattern called pseudo-typedef antipattern.Here is the alternative:
class TokenUtil{
public static <T> TypeToken<T> newTypeToken(){
return new TypeToken<T>();
}
public static void main(String[] args) {
TypeToken<ArrayList<ServerTask>> typeTok = TokenUtil.newTypeToken();
Type type = typeTok.getType();
}
}
精彩评论