2

I have the following two classes:

class Generic<T> 
{

}

class Foo extends Generic<Student>
{

}

Inside the Generic I have a method which I need to get the name of the class that is being passed through T

Is this possible in Java? I know in C# I would just use typeof.

I have tried to do the following:

String className = new TypeToken<T>() {}.getClass().getName().toString();

This however does not provide the name and only provides Generic$1

Phorce
  • 4,424
  • 13
  • 57
  • 107
  • 1
    Check this https://stackoverflow.com/questions/6624113/get-type-name-for-generic-parameter-of-generic-class – Naghaveer R Jan 04 '19 at 18:16
  • You cannot usefully create a generic type token. You can get the class from a non-generic type token (e.g. `new TypeToken() {}` or `new TypeToken>() {}`) by looking at the type parameters of its generic supertype; but that type parameter is the type variable T for that type token. – Andy Turner Jan 04 '19 at 18:21
  • I reopened as it isn't a duplicate of the claimed question. In this case the type is accessible through `Foo` (although there is a requirement that the subclass not use a parameter here). – Tom Hawtin - tackline Jan 04 '19 at 18:51

4 Answers4

2

Use getGenericSuperclass() method and check if is a ParameterizedType then get all arguments with getActualTypeArguments() that returns a Type array, remember that a generic class may use more than one type like Map (Map<Object, Object>)

public Class<?> getGenericType(Object obj) {
    Class<?> genericType = null;
    Type gnrcType = obj.getClass().getGenericSuperclass();
    if (gnrcType instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) gnrcType;
        Type types[] = parameterizedType.getActualTypeArguments();

        if (types != null && types.length > 0) {
            Type type = types[0];
            if (type instanceof Class) {
                genericType = (Class<?>) type;
            }
        }
    }
    return genericType;
}
imperezivan
  • 761
  • 5
  • 13
0

I am assuming Generic<T> is always subclassed with a class that supplies the type parameter with an actual class, as in the Foo code. In that case the type information is still available using Class.getGenericSupertype. Here's a quick example.

import java.lang.reflect.*;

class Generic<T> 
{
    public String name() {
        Class<?> clazz = this.getClass();
        for (;;) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass == null) {
                throw new IllegalStateException();
            }
            if (superClass == Generic.class) {
                Type genericSuper = clazz.getGenericSuperclass();
                if (genericSuper instanceof ParameterizedType) {
                    ParameterizedType parameterized = (ParameterizedType)genericSuper;
                    Type[] params = parameterized.getActualTypeArguments();
                    if (params.length == 1) {
                        Type param = params[0];
                        if (param instanceof Class<?>) {
                            return ((Class<?>)param).getName();
                        }
                    }
                }
                throw new IllegalStateException();
            }
            clazz = superClass;
        }
    }
}

class Foo extends Generic<Student>
{

}
interface Student {}

interface Code {
   static void main(String[] args) {
       System.err.println(new Foo().name());
   }
}

That only deals with simple cases. For instance if Foo extends Generic<Set<Student>> then T isn't a Class.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
0

The easy way to get the name of generic class.

Define a Generic class

public class GenericClass<T> {

private final Class<T> type;

public GenericClass(Class<T> type) {
     this.type = type;
}

public Class<T> getType() {
    return this.type;
}

}

Use this generic class as a parameter

GenericClass type = new GenericClass(MyEntity.class);
exampleMethod(type);

In the exampleMethod method

public void exampleMethod(GenericClass type) {
    String fullName= type.getType().getCanonicalName(); //"com.example.MyEntity"
    String sortName= type.getType().getSimpleName(); //"MyEntity"
}
-1

C# and Java implement generics in very different ways.

C# has true generics and generic information at runtime. For backward compatibility reasons Java uses Type erasure - that is all the generic magic is in the compiler and it substitutes Object for T everywhere and at runtime you have no knowledge of generic type.

Java generics is still better than C++ templates which is no better than macro substitution at compile time. But Java can't do C# style generics without breaking backward compatibility with older version of runtime/bytecode.

In summary, you can't do this in Java.

Fakrudeen
  • 5,778
  • 7
  • 44
  • 70