Getting the .class of a generic class - java

Suppose you have a certain class C, which supports generics (so C<X> defines a type). I would like to do something like:
I'd like to obtain an instance of the Class<C<X>> class. I would say that this can be obtained through the following expression:
C<X>.class
But the compiler doesn't agree with me on that :P
Any hint?

This is not possible because of type erasure. The type parameters are not available at runtime - all the instances of C<Whatever> are actually instances of simply C. Therefore, you can only write a class literal like C.class.
However, if you subclass a generic class with defining a concrete type parameter, such as this:
class StringC extends C<String> {}
... it is actually possible to obtain the value of the type parameter (which is String) via reflection. See these blogs:
http://gafter.blogspot.cz/2006/12/super-type-tokens.html
http://www.artima.com/weblogs/viewpost.jsp?thread=208860

The closest you can get is
#SuppressWarning("unchecked")
Class<C<X>> cxClass = (Class<C<X>>) (Class) C.class
As generics are a compile time feature, this doesn't really do much as you can see.

Related

Java: Is it possible to compare a Class<?> object with a generic type parameter?

Suppose i have a list of classes:
List<Class<?>> classes;
If i now take one of these classes, lets say... the second, and would want to know if it represents the String class, i would do the following:
classes.get(1).equals(String.class)
but how can i know if it represents a generic type... say T?
class Foo<T> {
void someMethod(){
System.out.println(classes.get(1).equals(T)); //error: T cannot be resolved to a variable
}
}
i tried
T.class
(Class<T>)T
But nothing works. I would really like to know if this is possible, and if it is, how to achieve it.
Thanks for your attention! =)
This is not possible in Java, due to type erasure (http://en.wikipedia.org/wiki/Type_erasure) - T does not actually exist at runtime, it only exists as information for the compiler. The standard way of fixing this is to pass in a Class<T> from wherever T is reified and use that class object.
That's not possible,but instead you can provide some wrappers for your classes that will implement simple interface with one method that will return type, and then pass as generic type this interface.

I want to extends enum and object (generic)

Enum is in the java.lang.Enum, and Object is in the java.lang.Object. So, why is Enum not an Object? (I am getting a java.lang.ClassCastException)
I want to make a class that can accept both Object and Enum, like this...
public class MyType<T extends Enum<T>, Object> {
But this gives a compilation error. How can i make my MyType so that it can accept all (or both) type of classes? (Enum or Object)?
Since every Java object is instanceof Object, there is nothing better to do than declare MyType<?> with no bounds. That will capture any type, including an enum type.
An Enum is an Object. See http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Enum.html
So
MyType<Object> {
should work.
You can see on documentation:
Excepting Object, which has no superclass, every class has one and
only one direct superclass (single inheritance). In the absence of any
other explicit superclass, every class is implicitly a subclass of
Object.
You can conclude:
even not explicit your class will extends from Object;
if Object was a figured manner to represent other class, you will not be able since java doesn't support multiple-inheritance.
Simply by looking at the API for class object, you can read:
Class Object is the root of the class hierarchy. Every class has Object as a superclass.
All objects, including arrays, implement the methods of this class.
This means that Enum is an object (you can verify this by looking at the JavaDocs for Enum aswell). Hence, if you simply tell MyType to contain Object(MyType<Object>), it will accept Object and Enum as you originally posted.
Although notice that this might cause unwanted behavior and is considered unsafe, since ALL classes are objects.
I don't think you've stated your question correctly. I think you want your class to accept Enum or Object. When you put it like that you can see that is the only possible choice.
If you want it to accept Enum and Object, that is equivalent to saying 'accept Enum'. In which case > is the answer.

ParameterizedType.getRawType() returns j.l.r.Type, not Class<?>?

ParameterizedType parameterized =
(ParameterizedType) List.class.getMethod("iterator").getGenericReturnType();
Type raw = parameterized.getRawType();
ParameterizedType#getRawType() returns a Type, not a Class<?> (although I get that java.lang.Class now implements Type). Is there a good reason why getRawType() doesn't declare its return type to be Class<?>? Are there extreme cases where getRawType()'s result might not be a Class<?>?
It's enough of a thrashing to work with j.l.r.Type as it is; this seems like an instance in which they could have saved us one downcast.
It must return a Class object, there's no other way.
Why? Who knows, maybe some idealistic bias. If it returned Class, it would be the only appearance of Class in the new Type interfaces.
The real problem is the mixing of Class and Type. Previously, all types are represented in Class. It was already messy, but still tolerable. There weren't very many types.
With the new generic types, they should have designed a cleaner and true-to-specType hierarchy independent of Class. Instead they incorporated Class with Type and created more mess. The entire hierarchy just doesn't make sense. Anyone new to the subject and unaware of the history will be appalled by this nonsense.
I wouldn't hold the design of Type to a high standard. For example, ParameterizedType defines equals(), but not hashCode(). There's no way to have two implementations of ParameterizedType work in one hash map. And wildcard is also a type? Hell no.
And the name of the method getRawType() is just idiotic. It has nothing to do with raw type. It should be plainly named getClassOrInterface(). Would it be too verbose? Look at getActualTypeArguments() then. (And yeah, it returns actual arguments! Not fake ones!)
I was thinking about this, and I have a hunch. Perhaps they wanted to leave the possibility open for future craziness like this:
public class Z<L extends List<?>> {
L<Double> test;
}
This is not legal Java code, but I think it's clear what it would mean; new Z<ArrayList<?>>().test would be of type ArrayList<Double>.
If this were legal, ((ParameterizedType) test.getGenericType()).getRawType() would return a TypeVariable.
Sun's implementation of ParameterizedType has defined the getRawType() method to return Class<?>. So it clearly returns only Class<?>
However, on my classpath there are a few more implementations of ParameterizedType - from hibernate-validator, from aspectj, hibernate-annotations, jaxb. Some of them return Class<?>, some - Type. I don't know how they are used though.
There are other uses for the Type interface hierarchy than just the reflection api. For instance, a code generation library may define custom implementations. The JDK 8 itself has 3 different implementations of WildcardType. If ParameterizedType.getRawType() returned a Class instance, then you'd need to be able to create a Class instance whenever you want.
A Class is a very deeply-ingrained-in-the-JVM type that has bindings back to native-managed memory. To create a Class instance, you must have the byte code that defines the class. But in the case of a code generation library, the byte code doesn't even exist yet. If they had required the ParameterizedType to return a Class, it would limit the applicability of the Type interface hierarchy to only the reflection api.
This may not seem like a big deal, but neither is a cast.
ParameterizedType.getOwnerType() returns a Type as it may itself be either a Class or another ParameterizedType. It may in theory return a TypeVariable, as the following is valid Java:
<M extends Map<?,?>> M.Entry<?,?> first(M map) { ... }
However, it's compiled as a static reference to the type variable's erasure, in this case M.Entry would be compiled as Map.Entry. Instead of a TypeVariable, a call to getOwnerType() from the reflection api will be a Class, at least according to my tests.

How to inherit from a base generic class

Have a base genric class like ClassBase<T>
I found I can use
ClassDerived extends ClassBase
or
ClassDerived<T> extends Classbase<T>
So basically it means I can remove generics in the derived class, is that right?
Since all generic type information is erased in the compilation process, of course you can do that. However, most compilers will generate a warning unless the warning is turned off or suppressed. For instance, if I write class Foo extends HashMap {}, Eclipse reports: "HashMap is a raw type. References to generic type HashMap should be parameterized"
You can extend the raw version of a generic class, that's correct. Of course your second example:
ClassDerived<T> extends Classbase
... actually re-introduces a type parameter.

Java: getting inner type in nested parameterized types (reflection)

Most of the documentation regarding type erasure handling in Java assumes that the use case is handling a type like SomeType<ParamType>.
I am trying to process method parameter for the following method:
public void setOtherReferenceRanges(List<ReferenceRange<T>> referenceRanges)
When the container class is instantiated with a type DvQuantity, this signature should become
public void setOtherReferenceRanges(List<ReferenceRange<DvQuanitity>> referenceRanges) in runtime.
Using reflection one can see that the List has an actualTypeArgument which is ReferenceRange<T>. Since reflection uses class information, I would not expect it to give me ReferenceRange<DvQuantity>.
However, when I created the class containing this method, I passed the DvQuantity type as T. So the type filling in T should be available to Java runtime, but I could not find a way of getting it. I end up with a TypeVariableImpl object accessed via reflection, which does not seem to contain any useful data.
Can you think of any ways to discover this information in runtime?
When you say
when I created the class containing this method
I guess you mean when you create an object of that type, for example:
foo = new ContainerClass<DvQuantity>();
In that case, because of erasure, there is no way to recover the type DvQuantity.
However, if you create a class passing a type parameter to the superclass, like this
class DvQuantityContainerClass extends ContainerClass<DvQuantity> {...}
...
foo = new DvQuantityContainerClass();
Or, shorter, an inline anonymous subclass (which looks almost like the first example but with a subtle but important difference):
foo = new ContainerClass<DvQuantity>(){};
Then you can recover the type parameter, because you recover the type parameter used to extend a superclass at runtime. Unfortunately, Java itself doesn't provide an easy way to now get the type of the DvQuantityContainerClass.setOtherReferenceRanges method with the T filled in. For that, I've written gentyref, to do advanced reflection on generic types:
Method m = DvQuantityContainerClass.class.getMethod("setOtherReferenceRanges", List.class);
// this will return List<ReferenceRange<DvQuanity>>, like you are lookingn for
return GenericTypeReflector.getExactParameterTypes(m, DvQuantityContainerClass.class)
Generic type information is erased by the compiler and is not available at runtime. When I need to ensure a certain type at runtime I pass in a class argument:
public <T> void doSomething(T t, Class<T> c);
This is not always convenient or even possible, but for many cases it is possible.
So the type filling in T should be available to Java runtime, but I could not find a way of getting it.
Perhaps it's not entirely correct, but the way I think about it is that at runtime there is no actual class - just an object without a specific type which meets the interface of T. In other words, erasure happens not with objects, but instead with these nebulous (in the OOP world at least) type-things.
http://java.sun.com/docs/books/tutorial/java/generics/erasure.html
There are ways of capturing the type information inside the class itself (T types would need a method getUnderlyingType()... or something), but that's a bad idea. If you truly need to raw type of the object, I'd reconsider using generics.

Categories

Resources