Java: How to Get a Class Literal from a Generic Type

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.

Class literal with generics in java

First things first.

  1. I can only pass List.class or Map.class instead of also specifying the
    generic type. Based on that the result type is determined
    (implementation details are not provided).

You cannot specify the generic type in the class parameter because (Read this carefully) The generic type information is not present during the runtime. At runtime, you do not get List<String> or List<Integer> etc. At runtime, everything is just plain List.


A brief history on why it is like that.

Generics did not exist until Java 5. So when generics was introduced as a part of Java 5, the last thing they wanted to do was to tell the whole world to change from List to List<String> or List<Whatever_Object_They_Want>.

For example, let's say you are using a dependency which was compiled using Java 4, and a method in this dependency which returns just plain old List, you can still call that method using your code, which is compiled in Java 5. Only thing is you cannot cast it to any generic type.

Imagine if Java5 started supporting only generic types like List<> and not the plain old List. You would have to depend on all the vendors to provide the dependency which is compiled in Java5. This would cause chaos. So to avoid this, and to maintain backward compatibility with those using previous versions of java, the generic type information is stripped off when the code is compiled to byte code. This is called Type Erasure.

It is because of this reason, you cannot specify the generic type with class literals of any collection type.


Coming to your second point


  1. Since, the result type is derived using class literal i am getting the following compile time errors.

Type mismatch: cannot convert from List<Set<String>> to List<T>


Type mismatch: cannot convert from List<Map<String,Object>> to List<T>

If you go through the Generics documentation, the generic type List is different from List< Map< String, Object >>>. (That is why we have generics in Java. Eg., List<String> is different from List<Object>. You cannot cast List<String> to List<Object>)

However, since you want to return List<Map<>> in some cases and List<Set<>> in some other cases depending on the parameter, I suggest you to change the return type of the method serve to List<? extends Collection>

Using List<? extends Collection> as a return type/method argument means that this list can be a List of any of the subclasses of Collection. Please have a look at the hierarchy to get more information.

Hope this helped!

about Java Generics, how to get the class

From 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 get a Class literal from a generically specific Class

Someone left a short but concise answer here, which I was about to choose as the answer. Unfortunately, that person deleted that answer. If that person would be so kind to re-post that answer for me to select. In the mean time, let me state that answer and how I made use of it.

This is what I should have thought of,


Interface RazmatazString extends Razmataz<String>{}

GWT.create(RazmatazString.class);

Basing on the answer that was deleted,

I would have a basic Razmataz interface pair the does a lot of stuffs, which I am too lazy to repeat.

Abstract class Razmatazer{
.....

Interface Razmataz<T>{
// does a lot of RPC stuffs
}

Interface RazmatazAsync<T>{
// does a lot of RPC stuffs
}

RazmatazAsync<?> razmatazAsyncRPC;
}

The concept is not to instantiate razmatazAsyncRPC handle at the base class but at the derived class.

For T = String

StringRazmatazer extends Razmatazer{

Interface RazmatazStringAsync extends RazmatazAsync<String>{}
Interface RazmatazString extends Razmataz<String>{}

razmatazAsyncRPC = GWT.create(RazmatazString.class);
}

Otherwise I would have to repeat ~100 lines of code, ~50 each for Razmataz and RazmatazAsync, over various T parametric values of String, Map, Boolean, Integer, etc.

The basic premise for overcoming this hurdle was - I am lazy to repeat those lines.

How to determine the class of a generic type?

Still the same problems : Generic informations are erased at runtime, it cannot be recovered. A workaround is to pass the class T in parameter of a static method :

public class MyGenericClass<T> {

private final Class<T> clazz;

public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
return new MyGenericClass<U>(clazz);
}

protected MyGenericClass(Class<T> clazz) {
this.clazz = clazz;
}

public void doSomething() {
T instance = clazz.newInstance();
}
}

It's ugly, but it works.

how to get a class reference to parameterized type

Using .class literal with a class name, or invoking getClass() method on an object returns the Class instance, and for any class there is one and only one Class instance associated with it.

Same holds true for a generic type. A class List<T> has only a single class instance, which is List.class. There won't be different class types for different type parameters. This is analogous to how C++ implements generics, where each generic type instantiation will have a separate Class instance. So in Java, you can't do Set<Integer>.class. Java doesn't allow that because it doesn't make sense, and might give wrong intentions about number of Class instances.

However, if you want a Class<Set<Integer>>, you can achieve that will a bit of type casting (which will be safe), as shown below:

Class<Set<Integer>> clazz = (Class<Set<Integer>>)(Class<?>) Set.class;

This will work perfectly fine.

How to retrieve the class of a generic type

The simple answer is: you have to pass a Class<T> argument to the method.

public <T> List<T> methodeName(String path, Class<T> clazz) throws FileNotFoundException {
CSVReader reader = new CSVReader(new FileReader(path), ',', '\"');

HeaderColumnNameTranslateMappingStrategy<T> strat =
new HeaderColumnNameTranslateMappingStrategy<T>();
strat.setType(clazz);
}

...

ClassName cn = new ClassName();
List<Foo> foos = cn.methodeName(somePath, Foo.class);

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.



Related Topics



Leave a reply



Submit