How to Create a Class Literal of a Known Type: Class<List<String>>

How to create a class literal of a known type: ClassListString

You can always cast to what you need, like this

return (Class<List<String>>) new ArrayList<String>().getClass();

or

return (Class<List<String>>) Collections.<String>emptyList().getClass();

But I assume that's not what you are after. Well it works, with a warning, but it isn't exactly "beautiful".

I just found this

Why is there no class literal for wildcard parameterized types?

Because a wildcard parameterized type has no exact runtime type representation.

So casting might be the only way to go.

Java: how do I get a class literal from a generic type?

You can't due to type erasure.

Java generics are little more than syntactic sugar for Object casts. To demonstrate:

List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal

The only instance where generic type information is retained at runtime is with Field.getGenericType() if interrogating a class's members via reflection.

All of this is why Object.getClass() has this signature:

public final native Class<?> getClass();

The important part being Class<?>.

To put it another way, from the Java Generics FAQ:

Why is there no class literal for concrete parameterized types?


Because parameterized type has no exact runtime type representation.

A class literal denotes a Class
object that represents a given type.
For instance, the class literal
String.class denotes the Class
object that represents the type
String and is identical to the
Class object that is returned when
method getClass is invoked on a
String object. A class literal can
be used for runtime type checks and
for reflection.

Parameterized types lose their type
arguments when they are translated to
byte code during compilation in a
process called type erasure . As a
side effect of type erasure, all
instantiations of a generic type share
the same runtime representation,
namely that of the corresponding raw
type . In other words, parameterized
types do not have type representation
of their own. Consequently, there is
no point in forming class literals
such as List<String>.class ,
List<Long>.class and List<?>.class
, since no such Class objects exist.
Only the raw type List has a Class
object that represents its runtime
type. It is referred to as
List.class.

How to do `MyClassString.class` in Java?

Use List.class. Because of type erasure type parameters to Java classes are entirely a compile-time construct - even if List<String>.class was valid syntax, it would be the exact same class as List<Date>.class, etc. Since reflection is by nature a runtime thing, it doesn't deal well with type parameters (as implemented in Java).

If you want to use the Class object to (for example) instantiate a new List instance, you can cast the result of that operation to have the appropriate type parameter.

List<String> list = (List<String>)(ArrayList.class.newInstance());

Getting Class of list with generic eg: ListNumber::class

Due to generic type erasure List class has a single implementation for all its generic instantiations. You can only get a class corresponding to List<*> type, and thus create only Builder<List<*>>.

That builder instance is suitable for building a list of something. And again due to type erasure what that something is you can decide by yourself with the help of unchecked casts:

Builder(List::class.java) as Builder<List<Number>>
Builder(List::class.java as Class<List<Number>>)

Another approach is to create inline reified helper function:

inline fun <reified T : Any> Builder() = Builder(T::class.java)

and use it the following way:

Builder<List<Number>>()

How to make a new List in Java

List myList = new ArrayList();

or with generics (Java 7 or later)

List<MyType> myList = new ArrayList<>();

or with generics (Old java versions)

List<MyType> myList = new ArrayList<MyType>();

What is the correct way to obtain the class object for class List without losing type information?

There is no way you can win this war.

There is no such thing as an instance of Class<List<?>>, or whatever. That's a valid type, but there is no concrete value that you can assign to that (other than null), because List.class and new ArrayList<>().getClass() have type Class<List> and Class<? extends List> respectively.

Class instances are inherently raw-typed, because of type erasure.

You can do unchecked casts:

Class<List<?>> clazz = (Class<List<?>>) (Class<?>) List.class;

but that will also generate warnings; and since you can do other unsafe casts, like:

Class<List<?>> clazz1 = (Class<List<?>>) (Class<?>) ArrayList.class;

you might end up in an odd situation where two instances of Class<List<?>> are unequal, or for example that sometimes a Class<List<?>> can be instantiated (using clazz.newInstance()), and other times not.


Now, if you need some sort of generic type token, you can use something like what Guice does:

TypeLiteral<ArrayList<?>> typeLiteral =
new TypeLiteral<ArrayList<?>>() {};

It is possible to obtain the ArrayList<?> from that at runtime, because of the way the superclass is captured (note the {} - this is an anonymous subclass of TypeLiteral). You can implement this yourself quite easily if you don't want to take a dependency on Guice (or other libraries which offer similar constructs), using typeLiteral.getClass().getGenericSuperclass().

But whether that is an approach you can take depends on your undisclosed problem. The key takeaway is that generics and reflection just don't play nicely together.

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorial for more details.

A popular solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.

class Foo<T> {
final Class<T> typeParameterClass;

public Foo(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}

public void bar() {
// you can access the typeParameterClass here and do whatever you like
}
}


Related Topics



Leave a reply



Submit