ChatGPT解决这个技术问题 Extra ChatGPT

Get generic type of class at runtime

How can I achieve this?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Everything I have tried so far always returns type Object rather than the specific type used.


H
Henning

As others mentioned, it's only possible via reflection in certain circumstances.

If you really need the type, this is the usual (type-safe) workaround pattern:

public class GenericClass<T> {

     private final Class<T> type;

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

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

I like this answer but it's a little cumbersome to instantiate: GenericClass g = new GenericClass(AnotherClass.class);
Its even more verbose if you use a dao/factory/manager approach. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
Thats true, but not working in all cases like stateless remote beans which are instantiated by container/reflection.
Just as a follow-up to my previous comment - after lot of pain playing with reflection, I ended up using this answer.
You can get around the superfluous reference by providing a generic static factory method. Something like public static <T> GenericClass<T> of(Class<T> type) {...} and then call it as such: GenericClass<String> var = GenericClass.of(String.class). A bit nicer.
F
FrVaBe

I have seen something like this

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

in the hibernate GenericDataAccessObjects Example


This technique works where the type parameter is defined on the immediate superclass, but it fails if the type parameter is defined elsewhere in the type hierarchy. For handling more complex cases something like TypeTools can be used. The docs include an example of a more sophisticated Generic DAO.
This only returns the actual type parameters used when a CLASS implements / extends something that has generic declarations, it does not return the actual type parameters used when an INSTANCE is instantiated. In other words, it CAN tell that in class A implements Comparable<String>, the actual type parameter is String, but it CANNOT tell that in Set<String> a = new TreeSet<String>(), the actual type parameter is String. In fact, the type parameter information is "erased" after compilation, as explained in other answers.
I am getting java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType for this answer.
This approach can also be achieved using Class-Mate from the Jackson folks. I wrote a gist here gist.github.com/yunspace/930d4d40a787a1f6a7d1
@TomášZato Calling simply the code above returned the same exception for me. I know it's a bit late, but anyway, in my case, I had to call (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments() to get to actual type arguments.
J
Jacob van Lingen

Generics are not reified at run-time. This means the information is not present at run-time.

Adding generics to Java while mantaining backward compatibility was a tour-de-force (you can see the seminal paper about it: Making the future safe for the past: adding genericity to the Java programming language).

There is a rich literature on the subject, and some people are dissatisfied with the current state, some says that actually it's a lure and there is no real need for it. You can read both links, I found them quite interesting.


Of course we are dissatisfied, .NET has much better generic handling mechanism
@Pacerier: but reified generics alone would not bring Java to the level of .NET. Value types an specialized code for those is at least equally important for why .NET is better in the generics area.
@JoachimSauer, yes value types. I'd always wanted those in java. Btw what do you mean by specialized code?
@spaaarky21 No, generic type parameters are removed during compilation (so-called "erasure", you can google it). The trick in FrVaBe's answer works only if the type params of the superclass are known statically (see the first commment by Johnathn)
Java type erasure is a historical design flaw; more code has been written to get around it than was written to implement it.
C
Cody A. Ray

Use Guava.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

A while back, I posted some full-fledge examples including abstract classes and subclasses here.

Note: this requires that you instantiate a subclass of GenericClass so it can bind the type parameter correctly. Otherwise it'll just return the type as T.


The constructor TypeToken(Type) is not visible
Notice that I create an empty anonymous subclass (see the two curly braces at the end). This uses reflection to battle Java's runtime type erasure. You can learn more here: code.google.com/p/guava-libraries/wiki/ReflectionExplained
@CodyA.Ray Your code throws a java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. So I changed the line new TypeToken(getClass()) { } to new TypeToken<T>(getClass()) { }. Now, code runs fine, but Type is still 'T'. See this: gist.github.com/m-manu/9cda9d8f9d53bead2035
@Dominik Please see the updated example that you can copy and paste to test yourself. I've also added a note clarifying that you must instantiate a subclass (as shown). As a general etiquette advise, please read any linked articles and related javadocs before you accuse a poster of "wishful thinking". I've used similar production code multiple times. The Guava helpers I'm demonstrating are intended for this exact use case and their javadocs show almost an exact answer to this question. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
@CodyA.Ray Since this only works with subclasses of GenericClass, you should make that class abstract so wrong usage doesn’t compile.
j
josefx

Java generics are mostly compile time, this means that the type information is lost at runtime.

class GenericCls<T>
{
    T t;
}

will be compiled to something like

class GenericCls
{
   Object o;
}

To get the type information at runtime you have to add it as an argument of the ctor.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

Example:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;

private final Class<T> type;
How can I create an array type from it: Type t = //String[]
@PawelCioch java.lang.reflect.Array.newInstance(elementtype, length); hope this helps (javadoc can be found here docs.oracle.com/javase/8/docs/api/java/lang/reflect/… )
@PawelCioch missed a .getClass() to get the type from the created array. There doesn't seem to be a direct way to get an array class. Most Java collections just use Object[] instead.
j
jpaugh

Sure, you can.

Java does not use the information at run time, for backwards compatibility reasons. But the information is actually present as metadata and can be accessed via reflection (but it is still not used for type-checking).

From the official API:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

However, for your scenario I would not use reflection. I'm personally more inclined to use that for framework code. In your case I would just add the type as a constructor param.


getActualTypeArguments only returns the type arguments for the immediate class. If you have a complex type hierarchy where T could be parameterized anywhere in the hierarchy, you'll need to do a bit of work to figure out what it is. This is more or less what TypeTools does.
This answer could be improved by adding some context or a code sample. I see method in the docs and I can find it actualTypeArguments via my IDE's debugger but I cannot figure out how to actually access it.
D
DevDio
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}

I'm upvoting this answer because it's a solution that works for the question being asked. However, for those who wants to navigate upward in class hierarchy like myself with more than one Generic class, this won't work. Because you will get java.lang.object instead of actual class.
Please note that this solution works ONLY if the class that holds the generic type is ABSTRACT
@JRA_TLL you apparently did something wrong. I just used it with Java 12 and works like a charm.
If you want to navigate up in view hierarchy you can cast the genericSuperclass to Class<*> and get the genericSuperclass. Prefereably in a loop.
M
Moesio

I used follow approach:

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}

I got "Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType" with this sample code
D
Dimitar

I dont think you can, Java uses type erasure when compiling so your code is compatible with applications and libraries that were created pre-generics.

From the Oracle Docs:

Type Erasure Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. Insert type casts if necessary to preserve type safety. Generate bridge methods to preserve polymorphism in extended generic types. Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html


Jup, it's impossible. Java would need reified generics for that to work.
It's entirely possible, see multiple other answers.
O
Ondrej Bozek

Technique described in this article by Ian Robertson works for me.

In short quick and dirty example:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }

At which specific line in this code are actual, run-time type parameters being read ?
here? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
D
DaBlick

I think there is another elegant solution.

What you want to do is (safely) "pass" the type of the generic type parameter up from the concerete class to the superclass.

If you allow yourself to think of the class type as "metadata" on the class, that suggests the Java method for encoding metadata in at runtime: annotations.

First define a custom annotation along these lines:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

You can then have to add the annotation to your subclass.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

Then you can use this code to get the class type in your base class:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

Some limitations of this approach are:

You specify the generic type (PassedGenericType) in TWO places rather than one which is non-DRY. This is only possible if you can modify the concrete subclasses.


Yes, it's non-DRY, however it's cleaner than the extension approach suggested above. I liked it. thanks
P
PJWeisberg

Here's one way, which I've had to use once or twice:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

Along with

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}

This technically works, however it doesn't solve the general case, and I think that is what the original poster is after.
This doesn't deserve to be voted down like is has - the original poster hasn't been explicit. This answer offers a design pattern that does work and is easy to implement, provided it is suitable to make the generic class abstract.
M
Matthias M

This is my solution:

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}

This is not an answer for this question.
My code is not the exact solution for the question. It returns the generic type parameters of the class, but not the actual type of T. But it may be helpful for others who stumple upon the question and are looking for my solution.
getClass().getGenericSuperclass() will achieve the same effect.
Y
Yaroslav Kovbas

Here is working solution!!!

@SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    } 

NOTES: Can be used only as superclass
1. Has to be extended with typed class (Child extends Generic<Integer>)
OR

2. Has to be created as anonymous implementation (new Generic<Integer>() {};)


ClassCastException
a
andrewmu

You can't. If you add a member variable of type T to the class (you don't even have to initialise it), you could use that to recover the type.


Oops, ok. You do have to initialise it from a constructor.
p
perror

One simple solution for this cab be like below

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}

This requires that an object be passed which may not always be possible.
g
galex

To complete some of the answers here, I had to get the ParametrizedType of MyGenericClass, no matter how high is the hierarchy, with the help of recursion:

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}

ClassCastException
L
Lin Yu Cheng

Here is my solution

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

No matter how many level does your class hierarchy has, this solution still works, for example:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

In this case, getMyType() = java.lang.String


This is not returning the type of T. It is returning T not java.lang.String besides the code is failing to covert Type to Class
Here is a online sample I made. Click compile and execute, then you can get the result. tutorialspoint.com/…
Works for me - when WildFly Weld CDI broke an alternative method.
I got Exception in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
No matter how many level does your class hierarchy has, but stop for one level class with generics...
H
HRgiger

Here is my trick:

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}

Note: this does not work when T is a type variable. In the case that T is a type variable, the varargs creates an array of erasure of T. See e.g. http://ideone.com/DIPNwd.
This returns "Object"
May be you're trying to answer some other question 🤔
P
Pablo Trinidad

Just in case you use store a variable using the generic type you can easily solve this problem adding a getClassType method as follows:

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

I use the provided class object later to check if it is an instance of a given class, as follows:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}

This is problematic, unfortunately. First of all, what if value is null? Second of all, what if value is a subclass of T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType(); returns Integer.class when it should return Number.class. It would be more correct to return Class<? extends T>. Integer.class is a Class<? extends Number> but not a Class<Number>.
P
PetermaxX

Here is my solution. The examples should explain it. The only requirement is that a subclass must set the generic type, not an object.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}

What is the TypeUtils?
k
kayz1
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

C
CoolMind

If you have a class like:

public class GenericClass<T> {
    private T data;
}

with T variable, then you can print T name:

System.out.println(data.getClass().getSimpleName()); // "String", "Integer", etc.

I tried this and got NullObject rather than the generic type.
@nasch, maybe data == null. In this case we cannot get a type.
Exactly so. If you already have an instance of the type, getting its class is no problem. The question is how to get it just from the class definition.
s
scruel

If you are working with spring:

public static Class<?>[] resolveTypeArguments(Class<?> parentClass, Class<?> subClass) {
    if (subClass.isSynthetic()) {
        return null;
    }
    return GenericTypeResolver.resolveTypeArguments(subClass, parentClass);
}

By the way, GenericTypeResolver will still get null for the non-subclasses class like the question mentioned, because the generic info of such class was completely erased after compilation.

The only way to solve this question may be:

public class GenericClass<T>
{
    private final Class<T> clazz;
    public Foo(Class<T> clazz) {
        this.clazz= clazz;
    }
    
    public Type getMyType()
    {
        return clazz;
    }
}

S
Shtefan

If you cannot change the generic class and use one of the method already explained on this page, then simple approach would be to get the type class based on the runtime instance class name.

Class getType(GenericType runtimeClassMember){
if (ClassA.class.equals(runtimeClassMember.getClass()){
  return TypeForClassA.class;
} else if (ClassB.class.equals(runtimeClassMember.getClass()){
  return TypeForClassB.class;
} 

//throw an expectation or do whatever you want for the cases not described in the if section.
}

O
Odwori

Use an abstract method that returns the class type then use it in that class and wherever you extend generic class you will have to implement that abstract method to return the required class type

public class AbsractService<T>{
  public abstract Class<T> getClassType ();
   .......
}

at runtime

class AnimalService extends AbstractService<MyType>{
    
public Class<MyType> getClassType (){
        return MyType.class;
 }

  .....
}

B
Bonestack

I did the same as @Moesio Above but in Kotlin it could be done this way:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}

j
jhyry-gcpud

This was inspired by Pablo's and CoolMind's answers. Occasionally I have also used the technique from kayz1's answer (expressed in many other answers as well), and I believe it is a decent and reliable way to do what the OP asked.

I chose to define this as an interface (similar to PJWeisberg) first because I have existing types that would benefit from this functionality, particularly a heterogeneous generic union type:

public interface IGenericType<T>
{
    Class<T> getGenericTypeParameterType();
}

Where my simple implementation in a generic anonymous interface implementation looks like the following:

//Passed into the generic value generator function: toStore
//This value name is a field in the enclosing class.
//IUnionTypeValue<T> is a generic interface that extends IGenericType<T>
value = new IUnionTypeValue<T>() {
    ...
    private T storedValue = toStore;
    ...
    
    @SuppressWarnings("unchecked")
    @Override
    public Class<T> getGenericTypeParameterType()
    {
        return (Class<T>) storedValue.getClass();
    }
}

I imagine this could be also implemented by being built with a class definition object as the source, that's just a separate use-case. I think the key is as many other answers have stated, in one way or another, you need to get the type information at runtime to have it available at runtime; the objects themselves maintain their type, but erasure (also as others have said, with appropriate references) causes any enclosing/container types to lose that type information.


Why the downvote? Given type erasure, the type needs to be stored at runtime to fully preserve it, and in some advanced cases there should be a managed way to handle cases where full type information at runtime is required. I ran into this situation pulling native values with JNA, where Java type information needs to be inferred and handled manually from native type indicators and byte values from native space. I would need more than a downvote to consider this to not be a valid use case for something like this in a very limited set of circumstances.
T
TheLetch

It might be useful to someone. You can Use java.lang.ref.WeakReference; this way:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

How's this class supposed to be used? Why WeakReference? Please provide some explanation with your answer, not just some code.
So if you have a SomeClass<MyClass> you can instantiate SomeClass and call getType on that instance and have the runtime to be MyClass.
Sure, but why WeakReference? What you said is not different from most of the other answers.
First my approach is shorter(less code), second the Weak references do not prevent their referents from being made finalizable, and as far as I know it doesn't use reflection, thus it's fast
This does not get the type of anything, this returns an object of that type, which, fyi, you can do with literally any kind of wrapper (AtomicReference, List, Set).
M
Mark Karchner

I found this to be a simple understandable and easily explainable solution

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}

Explain (T...t). (That's why this code doesn't work.)