What is the best way to retrieve the runtime value of a generic parameter for a generic class? For example:
public class MyClass<T> {
public void printT() {
// print the class of T, something like:
// System.out.println(T.class.getName());
}
}
So if I call
new MyClass<String>().printT()
it will print "String"
You don't. Due to type erasure that information is (mostly) lost at runtime. If you really need the class this is what you do:
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> c) {
if (c == null) {
throw new NullPointerException("class cannot be null");
}
clazz = c;
}
public void printT() {
System.out.println(clazz.getName());
}
}
and then you have access to it.
To achieve that, you need to add the type info, since type erasure means that T's type is not available.
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> clazz) {
this.clazz=clazz;
}
public void printT() {
// print the class of T, something like:
System.out.println(this.clazz);
}
}
Java doesn't have that information at runtime, because of type erasure. You would need to pass the type as a constructor parameter to the class when the object is instantiated. There are some libraries and languages which can help you to do that with less typing: Guice can do it and also Scala can do it.
As mentioned before, you can't obtain type information at that example because of type erasure.
However, you can redesign your classes hierarchy in order to have a generic superclass/interface and make child classes directly define type parameter at their definitions:
package com;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
public class AAA {
public static void main(String[] args) throws Exception {
Object target = new MyClass<Integer>() {}; // child class that explicitly defines superclass type parameter is declared here
ParameterizedType type = (ParameterizedType) target.getClass().getGenericSuperclass();
System.out.println(Arrays.toString(type.getActualTypeArguments()));
}
}
class MyClass<T> {
}
Related
I am tying to save the Type of a generic value, because i can't get it at runtime:
public class A<T> {
private final Class<T> genericType;
public A(Class<T> genericType) {
this.genericType = genericType;
}
public Class getGenericType() {
return genericType;
}
}
To make subclasses now, I use it as follows:
public class B extends A<String> {
public B() {
super(String.class);
}
}
Note thet the super()'s parameter type matches (by compile timne check) to the A's generic type.
That works fine. But if i want to have it with a Map, i cannot get the correct class object:
public class C extends A<Map<String, String>> {
public C() {
super(Map.class); // does not match Map<String,String>
super(Map<String,String>.class) // no valid java expression, i dont know what
}
}
Sooo anyone got a tip to help me out of this misery?
Best i could do currently, is to give up the strong typing in A:
public class A<T> {
// old: private final Class<T> genericType;
private final Class genericType; // note the missing generic
public A(Class genericType) { // here as well
this.genericType = genericType;
}
public Class getGenericType() {
return genericType;
}
}
I am not sure whether this satisfied your requirement, but you can do something similar as bellow, see How to get the class of a field of type T?
import java.lang.reflect.*;
import java.util.*;
public class GenericTypeTest{
public static void main(String []args){
B b = new B();
System.out.println("B is a " + b.getGenericType());
C c = new C();
System.out.println("C is a " + c.getGenericType());
}
}
class A<T> {
public Class getGenericType() {
Object genericType = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if(genericType instanceof ParameterizedType){
genericType = ((ParameterizedType)genericType).getRawType();
}
return (Class<T>) genericType;
}
}
class B extends A<String> {
}
class C extends A<Map<String,String>> {
}
this will get output something like
B is a class java.lang.String
C is a interface java.util.Map
There is just one Class object representing Map at runtime, which you can get by evaluating Map.class. There are no separate Class objects representing Map<String, String> or Map<Integer, Integer> or whatever at runtime. If you just want to take Map.class, which normally has type Class<Map>, and force that into Class<Map<String, String>>, you could do that via some unchecked casts:
super((Class<Map<String, String>>)(Class<?>)Map.class);
But whether that would do what you want depends on what you expect to do with your variable of type Class<T>, genericType, which you have not shown. For example, if you will use its .isInstance() method to check whether an object is an instance of T at runtime, know that since we don't know the generic type arguments of objects at runtime, we will only be able to check the raw type of the object, and not its type argument. This is the same reason why you can do foo instanceof Map or foo instanceof Map<?> but not foo instanceof Map<String, String>.
Or, maybe you want to use its .cast() method to do a type check on an object at runtime, which normally throws an exception if the object is not an instance of the Class's class, and if it is, it returns the object, but with compile-time type T. But here, again, you can't check at runtime whether an object is an instance of a parameterized type like Map<String, String>; you can only check whether it is a Map. So by it allowing you to get the result as type T without warning might be unsafe, as you get a compile-time expression of type Map<String, String>, but it might really be a Map<Integer, Integer>. This is the same reason why an unchecked cast like (Map<String, String>)foo causes an unchecked cast warning. It is unsafe and might cause unexpected problems elsewhere in your code.
I'm primarily a Java programmer, so this would be one of those "what is this thing from Java equivalent to in C#" questions. So, in Java, you can restrain a Class type argument at compile time to extend a certain super-class, like so:
public <T extends BaseClass> void foo(Class<T> type) {
...
}
and even
public <T extends BaseClass> T foo(Class<T> type) {
...
}
You can even chain multiple interfaces:
public <T extends BaseClass & BaseInterface1 & BaseInterface2> void foo(Class<T> type) {
...
}
How is this done in C#? I know you can use "where T : BaseClass", but this is only applicable when you have an instance T. What about when you only have a Type instance?
EDIT:
For explanation, here is what I would like to do:
ASSEMBLY #1 (base.dll):
abstract class BaseClass {
abstract void Foo();
}
ASSEMBLY #2 (sub1.dll, references base.dll):
class SubClass1 : BaseClass {
void Foo() {
// some code
}
}
ASSEMBLY #3 (sub2.dll, references base.dll):
class SubClass2 : BaseClass {
void Foo() {
// some other code
}
}
ASSEMBLY #4 (main.dll, references base.dll):
class BaseClassUtil {
static void CallFoo(Type<T> type) where T : BaseClass {
T instance = (T)Activator.CreateInstance(type);
instance.Foo();
}
}
public static void Main(String[] args) {
// Here I use 'args' to get a class type,
// possibly loading it dynamically from a DLL
Type<? : BaseClass> type = LoadFromDll(args); // Loaded from DLL
BaseClassUtil.CallFoo(type);
}
So, in this example, I don't care what class the 'type' variable represents, as long as it is derived from BaseClass, so once I create an instance, can call Foo().
The parts that are not vaild C# code (but rather some Java mockup) are the "generic" Type classes: Type<T> and Type<? : BaseClass>.
No, there is no way to enforce at compile time that a Type be assignable to a generic type. If I understand correctly, what you want is:
void Foo<T>(Type type) { ... } //compile time error if an instace typed `type` is not assignable to `T`.
Which means:
void Foo<IFormattable>(typeof(string)); //ok
void Foo<IDisposable>(typeof(string)); //compile time error
Evidently at runtime it is trival, but the language has no support for this at compile time.
From what I understood you are talking about generic type constraint
public void Foo<T>(Type type) where T:BaseClass, BaseInterface1, BaseInterface2
{
//your code
}
Here another article:Constraints on Type Parameters (C# Programming Guide)
When you define a generic class, you can apply restrictions to the
kinds of types that client code can use for type arguments when it
instantiates your class. If client code tries to instantiate your
class by using a type that is not allowed by a constraint, the result
is a compile-time error.
EDIT:
Here your example. Now if you try to call BaseClassUtil.CallFoo<T> with something different from BaseClass and his derived classes you will receive an compile error. Here full example in dotNetFiddle. So the tricky part is the restriction of your class should happen in the Util class
public static void Main(string[] args)
{
//so your LoadFromDll method should return Type. Type doesn't have generic implementation !
Type type = typeof(SubClass1);
BaseClassUtil.CallFoo<BaseClass>(type);
Type type2 = typeof(SubClass2);
//you can write BaseClassUtil.CallFoo<SubClass2>(type2); if you want
BaseClassUtil.CallFoo<BaseClass>(type2);
}
public class BaseClassUtil
{
public static void CallFoo<T>(Type type) where T : BaseClass
{
T instance = (T)Activator.CreateInstance(type);
instance.Foo();
}
}
public class TestClass
{
public int ID { get; set; }
}
public abstract class BaseClass
{
public abstract void Foo();
}
public class SubClass1 : BaseClass
{
public override void Foo()
{
Console.WriteLine("SubClass 1");
}
}
public class SubClass2 : BaseClass
{
public override void Foo()
{
Console.WriteLine("SubClass 2");
}
}
How can I convert java.lang.reflect.Type to Class<T> clazz?
If I have one method as next which has an argument of Class<T>:
public void oneMethod(Class<T> clazz) {
//Impl
}
Then another method which has an argument of java.lang.reflect.Type and it calls oneMethod(Class<T> clazz) and for it I need to convert java.lang.reflect.Type type to Class<T>:
public void someMehtod(java.lang.reflect.Type type) {
// I want to pass type arg to other method converted in Class<T>
otherMethod(¿How to convert java.lang.reflect.Type to Class<T>?);
}
Is it possible?
You have to ensure that type is an instance of Class, and then cast it.
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
otherMethod(clazz);
}
Of course, you also have to handle the case of it not being a Class.
If you are willing to use a library, you could use com.google.guava:guava:12+:
Class<?> clazz = com.google.common.reflect.TypeToken.of(type).getRawType();
Alternatively you could also use com.fasterxml.jackson.core:jackson-databind:2.8.x:
Class<?> clazz = com.fasterxml.jackson.databind.type.TypeFactory.rawClass(type);
This handles all cases correctly and you will get the type-erased class of your type.
Using generic types in runtime is a little bit tricky in Java. And I think this is a root cause of your issue.
1) to be sure about generic in runtime we doing like this:
class MyClass<E> {}
and then:
MyClass<TargetType> genericAwaredMyClassInctance = new MyClass<TargetType>(){};
please pay attention to {} in the end. It means anonymous extends of MyClass. This is an important nuance.
2) let`s improve MyClass to be able to extract the type in runtime.
class MyClass<E> {
#SuppressWarnings("unchecked")
protected Class<E> getGenericClass() throws ClassNotFoundException {
Type mySuperclass = getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
String className = tType.getTypeName();
return (Class<E>) Class.forName(className);
}
}
and finally, use it like this:
MyClass<TargetType> genericAwaredMyClassInctance = new MyClass<TargetType>(){};
assert(genericAwaredMyClassInctance.getGenericClass() == TargetType.class)
Andy Turner answer is correct, however if you need to retrieve a class from a type parameter this is a complete example:
private static class MyClass extends ArrayList<Integer> {
}
public static void main(String[] args) {
ParameterizedType arrayListWithParamType
= (ParameterizedType) MyClass.class.getGenericSuperclass();
Type integerType = arrayListWithParamType.getActualTypeArguments()[0];
Class<?> integerClass = (Class<?>) integerType;
System.out.println(integerClass == Integer.class);
}
It would be weird that a Type would be anything else than a Class... Javadoc for Type says
All Known Implementing Classes:
Class
So unless you have special libraries that use non Class Types, you can simply cast - but you must be ready for a possible ClassCastException.
Beware: Java use undocumented Type implementation to represent generics, see below.
You can explicitely process it or not because it is an unchecked exception:
Explicit way:
try {
Class<?> clazz = (Class<?>) type;
}
catch (ClassCastException ex) {
// process exception
}
Implicit way:
Class<?> clazz = (Class<?>) type;
but the current method could throw...
EDIT per #Andy Turner's comment:
Beware: Type type = new ArrayList<String>().getClass().getGenericSuperclass(); yields something that's a Type but not a Class. This one is a ParameterizedType, so you can use getRawType() method to find the actual class, but others might exist.
Did you mean this?
public <T extends Type> void oneMethod(T clazz) {
}
public void someMethod(Type type) {
oneMethod(type);
}
Isn't there any way to find the class-type of a generic?
if (T instanceof String) {
// do something...
}
The above definitely does not compile.
Generics are a compile time feature. Generics add checks at compile time which may not have any meaning at runtime. This is one example. You can only check the type of the object referenced which could be a super type in code. If you want to pass the type T you have do this explicitly.
void someMethod(Class<T> tClass) {
if(String.class.isAssignableFrom(tClass))
or
void someMethod(Class<T> tClass, T tArg) {
Note: the type might not be the same,
someMethod(Number.class, 1);
It won't compile because T is not a variable, but a place holder for a class that is defined at runtime. Here's a quick sample:
public class Test<T> {
public void something(T arg) {
if (arg instanceof String) {
System.out.println("Woot!");
}
}
public static void main(String[] args) {
Test<String> t = new Test<String>();
t.something("Hello");
}
}
if you have subclass
public class SomeClass extends SomeSubclass<String>{}
and
public class SomeSubclass<T> {}
then there is a way to discover type of T by executing code
Type t = getClass().getGenericSuperclass()
if (t instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType)t).getActualTypeArguments()
// in simple cases actualTypeArguments will contain Classes, since Class implements Type
}
if your case are a bit more complex (? extends String)` take a look at org.ormunit.entity.AEntityAccessor#extractClass
If you have specific field you can just check it like below:
private <T> String someMethod(T genericElement)
{
if (String.class.isInstance(genericElement))
{
return (String) genericElement;
}
...
I have a generic class, says :
MyClass<T>
Inside a method of this class, I would like to test the type of T, for example :
void MyMethod()
{
if (T == String)
...
if (T == int)
...
}
how can I do that ?
Thanks for your help
You can't, normally, due to type erasure. See Angelika Langer's Java Generics FAQ for more details.
What you can do is pass a Class<T> into your constructor, and then check that:
public MyClass<T>
{
private final Class<T> clazz;
public MyClass(Class<T> clazz)
{
this.clazz = clazz;
}
public void myMethod()
{
if (clazz == String.class)
{
...
}
}
}
Note that Java does not allow primitives to be used for type arguments though, so int is out...
Because of type erasure you can't... mostly. But there is one exception to that. Consider:
class A {
List<String> list;
}
public class Main {
public static void main(String args[]) {
for (Field field : A.class.getDeclaredFields()) {
System.out.printf("%s: %s%n", field.getName(), field.getGenericType());
}
}
}
Output:
list: java.util.List<java.lang.String>
If you need the class object, this is how you generally handle it:
public <T> T createObject(Class<T> clazz) {
return clazz.newInstance();
}
ie by passing the class object around and deriving the generic type from that class.
Additionally to cletus one exception I've mine: super type tokens. The super type token will preserve the type information.
new Type<Set<Integer>>() {}
The type information can be retrieved with Class.getGenericSuperClass.
if (object instanceof String)
System.out.println("object is a string");
As it was already stated you can get only generics-related information available at the static byte code level.
It's possible to resolve type arguments values and check if one type may be used in place of another then.
if you have subclass B extends A that should match, too, the approach clazz == A.class. Doesn't work. You should then use A.class.isInstance(b) where b is an object of type B.
If you want to do different things for different types would it still be generic?