单身人士工厂使用泛型和反射

时间:2017-06-04 13:42:20

标签: java generics reflection singleton factory

OrderDBManager,UserDBManager,ProductDBManager扩展DBManager。 我正在尝试创建一个单例DBManager工厂 获取不同DBManager类型类的实例。

但是我在使用泛型返回用户想要的精确DBManager类型类的实例时遇到了问题。现在它只适用于DBManager本身。 我很感激你的帮助!

public class DBManagerFactory {

     private static final Map<String, DBManager> instances = new HashMap<>();

     private static final String DB_MANAGER_PACKAGE_NAME = "ua.Test.db.";

     public static DBManager getInstance(Class<? extends DBManager> dbManagerClass) {
            String dbManagerClassName = dbManagerClass.getSimpleName();
            DBManager dbManager = instances.get(dbManagerClassName);

            if(dbManager == null){
                Class clazz = Class.forName(DB_MANAGER_PACKAGE_NAME + dbManagerClassName);
                dbManager = (DBManager)clazz.newInstance();
                instances.put(dbManagerClassName, dbManager);    
            }
            return dbManager;                                             
     }}                                                                

2 个答案:

答案 0 :(得分:1)

  

但是我使用泛型返回精确的实例时遇到问题   用户想要的DBManager类型。

要返回方法中的特定类,应指定一个作用域方法参数化类型,该类型允许类的客户端指定方法应返回的类型。

您可以用这种方式声明方法:

public static <T extends DBManager> T getInstance(Class<T> dbManagerClass)  {

为了避免不安全的强制转型,您可以使用DBManager方法将Map<String, DBManager>检索到的T extends DBManager实例强制转换为Class#cast(Object )类型在转换DBManager实例之前:

T dbManager = dbManagerClass.cast(instances.get(dbManagerClassName));

最后,检索类的方法:

Class clazz = Class.forName(DB_MANAGER_PACKAGE_NAME + dbManagerClassName);
由于您已将该类作为该方法的参数,因此不需要

直接使用它。

以下是实现此解决方案的示例代码:

public class DBManagerFactory {

    private static final Map<String, DBManager> instances = new HashMap<>();

    public static <T extends DBManager> T getInstance(Class<T> dbManagerClass)  {

      final String dbManagerClassName = dbManagerClass.getSimpleName();

      T dbManager = dbManagerClass.cast(instances.get(dbManagerClassName));

      if (dbManager == null) {
        try {
          dbManager =  dbManagerClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
          e.printStackTrace(); // or better log it
          throw new RuntimeException("faillure during reflection instantiation for class " + dbManagerClass.getName(), e);
        }
        instances.put(dbManagerClassName, dbManager);
      }

      return dbManager;
    }   

}

通过这些修改,客户端类可以这种方式调用该方法:

DBManager instance = DBManagerFactory.getInstance(DBManager.class);
DBManagerChild child = DBManagerFactory.getInstance(DBManagerChild.class);

答案 1 :(得分:0)

如果您需要获得显式返回类型,则可以使用generic factory method,如下所示:

class Base {}

class A extends Base {}

class B extends Base {}

class InitializationFailedException extends RuntimeException {
    public InitializationFailedException(Throwable cause) {
        super(cause);
    }
}

class Factory {

    private static Logger log = LoggerFactory.getLogger(Factory.class);

    private static final ConcurrentHashMap<Class<? extends Base>, Base> instances = new ConcurrentHashMap<>();

    public static  <T extends Base> T getInstance(Class<T> tClass) {
        if (tClass == null) {
            throw new NullPointerException();
        }
        return (T) instances.computeIfAbsent(tClass, (tClass1) -> {
            try {
                return tClass1.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                log.error("Unable to create singleton value for " + tClass1.getCanonicalName(), e);
                throw new InitializationFailedException(e);
            }
        });
    }

}

使用这种方法,您可以获得创建的子类型引用:

A aInstance = Factory.getInstance(A.class);
B bInstance = Factory.getInstance(B.class);

您的问题方法签名可能如下所示:

public static <T extends DBManager> T getInstance(Class<T> dbManagerClass);

实例Map应如下所示:

private static final Map<Class<? extends DBManager>, DBManager> instances = new HashMap<>();