mark
mark

Reputation: 62846

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

Upvotes: 9

Views: 6863

Answers (4)

user545680
user545680

Reputation:

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.

Upvotes: 13

Emil
Emil

Reputation: 13789

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();
   }

}

Upvotes: 0

Mister Smith
Mister Smith

Reputation: 28188

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

Upvotes: 1

stevevls
stevevls

Reputation: 10843

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.

Upvotes: 1

Related Questions