【问题标题】:Calling constructor of generic type?调用泛型类型的构造函数?
【发布时间】:2017-03-12 09:35:56
【问题描述】:

我正在尝试创建自己的存储库供我的数据库学习,所以我正在尝试这样的事情:

@Override
public <T extends DatabaseObject> List<T> getList() {
    Cursor cursor = getCursor(somehowGetClassOfT(), null, null); //how to do this?
    //excess code removed, rest of function not relevant to question
    return list;
}

protected <T extends DatabaseObject> Cursor getCursor(Class<T> clazz, String selection, String[] selectionArgs) {
    DatabaseObject databaseObject = instantiateFromT(clazz); //how to do this?
    String tableName = databaseObject.getTableName();
    String[] projection = databaseObject.getProjection();
    String sortOrder = databaseObject.getDefaultOrderColumn() + " " + DEFAULT_SORT_ORDER;
    Cursor cursor = database.query(
            tableName,                            
            projection,               
            selection,                             
            selectionArgs,                            
            null,                                     
            null,                                     
            sortOrder                                 
    );
    return cursor;
}

换句话说,我有很多扩展 DatabaseObject 的类,我希望能够为它们动态地构造一个 Cursor。

我已经在 DatabaseObject 中定义了基本方法,例如获取表名、列名的字符串数组等,但是由于我无法通过接口覆盖静态方法(例如表名),我必须实例化一个空白对象,以便我可以使用 getter 获取表名。

但是,我不确定如何实现:

  1. somehowGetClassOfT()。无论T 是什么,我都想将它的类传递给Cursor 函数。

  2. instantiateFromT(clazz)。给定某个类,调用构造函数,以便我可以访问该对象的表/投影/排序字段。

或者通过使用我听说过的“反射”来实现这一切?

【问题讨论】:

    标签: java class generics inheritance constructor


    【解决方案1】:

    泛型在运行时不会被具体化。由于信息不存在,您有两种解决方案:

    • 它可以通过在类中引入一个私有字段在编译时存储
    • 您可以使用反射在运行时从实例解析泛型类型。

    这是一个带有私有字段 T 和构造函数的示例,从您的代码开始:

    public abstract class MyRepository<T extends DatabaseObject> {
    
        private Class<T> type;
    
        public MyRepository(Class<T> type) {
          this.type = type;
        }
    
        public <T> List<T> getList() {
          Cursor cursor = getCursor(null, null); // how to do this?
          // excess code removed, rest of function not relevant to question
          return null;
        }
    
        protected <T extends DatabaseObject> Cursor getCursor(String selection, String[] selectionArgs) {
          DatabaseObject databaseObject = instantiateFromType(); // how to do this?
          String tableName = databaseObject.getTableName();
          String[] projection = databaseObject.getProjection();
          String sortOrder = databaseObject.getDefaultOrderColumn() + " " + DEFAULT_SORT_ORDER;
          Cursor cursor = database.query(
            tableName,
            projection,
            selection,
            selectionArgs,
            null,
            null,
            sortOrder);
          return cursor;
        }
    
        private DatabaseObject instantiateFromType() {
          try {
              T interfaceType = (T) type.newInstance();
              return interfaceType;
          }
          catch (Exception e) {
              // TODO to handle
           }         
        }
    }
    

    这里是你的 DogRepository:

    public class DogRepository extends MyRepository<Dog> {
    
        public DogRepository() {        
          super(Dog.class);
        }
    
    }
    

    使用实现 DatabaseObject 的 Dog:

    public class Dog implements DatabaseObject{
       //todo
    }
    

    回复您的评论。

    DogRepository(Class type)这一行,是否需要传入 类作为参数?

    不需要(对不起,我读了你的评论不好))。
    更进一步,您可能会注意到,具体存储库的构造函数是样板代码。
    如果使用反射来解析 DogRepository 中使用的泛型背后的类,则无需再为其定义自定义构造函数。

    例如,要检索存储库中使用的类型,Spring 使用自己的库使用 JDK 反射机制来完成这项工作:

    Map<TypeVariable, Type> source = GenericTypeResolver.getTypeVariableMap(DogRepository.class);
    

    Spring 有它的库,因为它使用了很多反射。
    但是在您的情况下,您也可以在不使用 lib 而是使用自己的实用程序类的情况下完成这项工作。这是一个从存储库实例解析泛型类型的示例。

    DogRepository dogRepository = new DogRepository();
    Class<?> typeClass = (Class<?>) ((ParameterizedType)   dogRepository.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    System.out.println("typeClass=" + typeClass );
    

    用反射来解析MyRepository构造函数中的有效泛型:

    public abstract class MyRepository<T extends DatabaseObject> {
    
        private Class<T> type;
    
        public MyRepository() {
           type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
       ...
     }
    

    存储库具体类不再需要自定义构造函数:

    public class DogRepository extends MyRepository<Dog> {
    
    }
    

    【讨论】:

    • 直截了当。我回答了。
    • DogRepository(Class&lt;Dog&gt; type)这一行,有必要把类作为参数传入吗? IE。 DogRepository 不应该以某种方式对 Dog.class 进行内部引用以传递参数,这样就不必每次调用存储库时都从外部传入?
    • 在构造函数中传入与参数化类型匹配的类。因此,就像在构造函数中一样,它在实例的生命周期中被调用一次。每次在存储库上调用方法时,都不会重播这些指令。你可以避免构造函数,但你应该使用反射。我将编辑我的答案给你一个例子。
    • 等等,什么?你为什么不写public DogRepository() { super(Dog.class); }
    • @LouisWasserman 这就是我的问题的意思,是的。我不明白为什么课程必须从外部传入。您可以直接将其传递给 super 调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-19
    • 2021-08-17
    • 2010-10-16
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    相关资源
    最近更新 更多