如何获得泛型类型t的类示例?

lokaqttq  于 2021-07-14  发布在  Java
关注(0)|答案(22)|浏览(663)

我有个泛型课, Foo<T> . 以一种 Foo ,我要获取类型的类示例 T ,但我就是打不来 T.class .
用什么方法绕过它 T.class ?

fjnneemd

fjnneemd1#

简单的回答是,在java中找不到泛型类型参数的运行时类型。我建议阅读java教程中有关类型擦除的一章以了解更多细节。
一个流行的解决办法是通过 Class 将类型参数的。

class Foo<T> {
    final Class<T> typeParameterClass;

    public Foo(Class<T> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }

    public void bar() {
        // you can access the typeParameterClass here and do whatever you like
    }
}
zkure5ic

zkure5ic2#

我一直在寻找一种方法来实现这一点,而不需要向类路径添加额外的依赖项。经过一些调查,我发现这是可能的,只要你有一个泛型超类型。这对我来说还可以,因为我正在使用一个带有泛型层超类型的dao层。如果这符合你的情况,那么这是最整洁的方法。
我遇到的大多数泛型用例都有某种泛型超类型。 List<T> 为了 ArrayList<T> 或者 GenericDAO<T> 为了 DAO<T> 等等。

纯java解决方案

在java中访问运行时泛型类型的文章解释了如何使用纯java来实现。

@SuppressWarnings("unchecked")
public GenericJpaDao() {
  this.entityBeanType = ((Class) ((ParameterizedType) getClass()
      .getGenericSuperclass()).getActualTypeArguments()[0]);
}

Spring解决方案

我的项目是使用spring,这是更好的,因为spring有一个方便实用的方法来查找类型。这对我来说是最好的方法,因为它看起来很整洁。我想如果你不使用spring,你可以编写自己的实用方法。

import org.springframework.core.GenericTypeResolver;

public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{

    @Autowired
    private SessionFactory sessionFactory;

    private final Class<T> genericType;

    private final String RECORD_COUNT_HQL;
    private final String FIND_ALL_HQL;

    @SuppressWarnings("unchecked")
    public AbstractHibernateDao()
    {
        this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
        this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
        this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
    }
lzfw57am

lzfw57am3#

但是有一个小漏洞:如果你定义你的 Foo 抽象类。这意味着您必须将类示例化为:

Foo<MyType> myFoo = new Foo<MyType>(){};

(注意末尾的双大括号。)
现在您可以检索 T 运行时:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

但是请注意 mySuperclass 必须是类定义的超类,该类定义实际上定义了 T .
它也不是很优雅,但你必须决定你是否喜欢 new Foo<MyType>(){} 或者 new Foo<MyType>(MyType.class); 在你的代码里。
例如:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;

/**
 * Captures and silently ignores stack exceptions upon popping.
 */
public abstract class SilentStack<E> extends ArrayDeque<E> {
  public E pop() {
    try {
      return super.pop();
    }
    catch( NoSuchElementException nsee ) {
      return create();
    }
  }

  public E create() {
    try {
      Type sooper = getClass().getGenericSuperclass();
      Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];

      return (E)(Class.forName( t.toString() ).newInstance());
    }
    catch( Exception e ) {
      return null;
    }
  }
}

然后:

public class Main {
    // Note the braces...
    private Deque<String> stack = new SilentStack<String>(){};

    public static void main( String args[] ) {
      // Returns a new instance of String.
      String s = stack.pop();
      System.out.printf( "s = '%s'\n", s );
    }
}
mnowg1ta

mnowg1ta4#

标准方法/解决方法/解决方案是添加 class 对象,例如:

public class Foo<T> {

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

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

    public T newInstance() {
      return type.newInstance();
    }
 }
nsc4cvqm

nsc4cvqm5#

假设您有一个抽象的泛型超类:

public abstract class Foo<? extends T> {}

还有第二个类用一个泛型条扩展foo,泛型条扩展t:

public class Second extends Foo<Bar> {}

你可以去上课 Bar.class 在foo类中选择 Type (来自伯特·布鲁尼的回答)并用 Class 示例:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);

必须注意,此操作并不理想,因此最好缓存计算值,以避免对此进行多次计算。一个典型的用法是在泛型dao实现中。
最终实施:

public abstract class Foo<T> {

    private Class<T> inferedClass;

    public Class<T> getGenericClass(){
        if(inferedClass == null){
            Type mySuperclass = getClass().getGenericSuperclass();
            Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
            String className = tType.toString().split(" ")[1];
            inferedClass = Class.forName(className);
        }
        return inferedClass;
    }
}

当从其他函数中的foo类或bar类调用时,返回的值是bar.class。

6psbrbz9

6psbrbz96#

以下是一个可行的解决方案:

@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 <> ");
    }
}

注意:只能用作超类
必须用类型化类进行扩展( Child extends Generic<Integer> )

必须创建为匿名实现( new Generic<Integer>() {}; )

mrfwxfqh

mrfwxfqh7#

我在一个抽象泛型类中遇到了这个问题。在这种特殊情况下,解决方案更简单:

abstract class Foo<T> {
    abstract Class<T> getTClass();
    //...
}

后来在派生类上:

class Bar extends Foo<Whatever> {
    @Override
    Class<T> getTClass() {
        return Whatever.class;
    }
}
ej83mcc0

ej83mcc08#

你不能这样做,因为类型删除。另请参阅堆栈溢出问题java泛型-类型擦除-何时发生以及发生什么。

pobjuy32

pobjuy329#

一个比其他人建议的类更好的方法是传入一个对象,这个对象可以执行您对该类所做的操作,例如,创建一个新示例。

interface Factory<T> {
  T apply();
}

<T> void List<T> make10(Factory<T> factory) {
  List<T> result = new ArrayList<T>();
  for (int a = 0; a < 10; a++)
    result.add(factory.apply());
  return result;
}

class FooFactory<T> implements Factory<Foo<T>> {
  public Foo<T> apply() {
    return new Foo<T>();
  }
}

List<Foo<Integer>> foos = make10(new FooFactory<Integer>());
xeufq47z

xeufq47z10#

对于这个问题,我有一个(丑陋但有效的)解决方案,它不需要外部依赖项/库:

import java.lang.reflect.TypeVariable;

public static <T> Class<T> getGenericClass() {
    __<T> instance = new __<T>();
    TypeVariable<?>[] parameters = instance.getClass().getTypeParameters(); 

    return (Class<T>)parameters[0].getClass();
}

// Generic helper class which (only) provides type information. This avoids the usage
// of a local variable of type T, which would have to be initialized.
private final class __<T> {
    private __() {
    }
}
5sxhfpxr

5sxhfpxr11#

有可能:

class Foo<T> {
  Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}

您需要两个来自hibernate generic dao/blob/master/dao/src/main/java/com/googlecode/genericdao/dao/daoutil.java的函数。
有关更多解释,请参见反射泛型。

9bfwbjaz

9bfwbjaz12#

我假设,既然你有一个泛型类,你会有这样一个变量:

private T t;

(此变量需要在构造函数处取一个值)
在这种情况下,您只需创建以下方法:

Class<T> getClassOfInstance()
{
    return (Class<T>) t.getClass();
}

希望有帮助!

idfiyjo8

idfiyjo813#

我找到了一个通用而简单的方法。在我的类中,我创建了一个方法,该方法根据泛型类型在类定义中的位置返回泛型类型。假设类定义如下:

public class MyClass<A, B, C> {

}

现在,让我们创建一些属性来持久化这些类型:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

// Getters and setters (not necessary if you are going to use them internally)

    }

然后可以创建一个泛型方法,该方法基于泛型定义的索引返回类型:

/**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {
        // To make it use generics without supplying the class type
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }

最后,在构造函数中,只需调用方法并发送每个类型的索引。完整的代码应该如下所示:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

    public MyClass() {
      this.aType = (Class<A>) getGenericClassType(0);
      this.bType = (Class<B>) getGenericClassType(1);
      this.cType = (Class<C>) getGenericClassType(2);
    }

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {

        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
}
jjhzyzn0

jjhzyzn014#

如其他答案所述,使用 ParameterizedType 方法,您需要扩展类,但这似乎是额外的工作,使一个全新的类扩展它。。。
因此,使类抽象化会迫使您对其进行扩展,从而满足子类化需求(使用lombok的@getter)。

@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

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

现在不需要定义新类就可以扩展它(注意结尾的{}。。。扩展,但不要覆盖任何内容(除非您愿意)。

private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();
u2nhd7ah

u2nhd7ah15#

这很直截了当。如果您需要来自同一班级:

Class clazz = this.getClass();
ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
try {
        Class typeClass = Class.forName( parameterizedType.getActualTypeArguments()[0].getTypeName() );
        // You have the instance of type 'T' in typeClass variable

        System.out.println( "Class instance name: "+  typeClass.getName() );
    } catch (ClassNotFoundException e) {
        System.out.println( "ClassNotFound!! Something wrong! "+ e.getMessage() );
    }

相关问题