【问题标题】:return Class.forName instead of new instance返回 Class.forName 而不是新实例
【发布时间】:2015-09-28 07:31:31
【问题描述】:

我在一个旧项目中找到了这段代码

public abstract class AireBatchDaoFactory {
     public static final String ORACLE_FACTORY = "airebatch.oracle.OracleDaoFactory";
     public static AireBatchDaoFactory getFactory(String factory) throws SQLException
     {
        try {
           return (AireBatchDaoFactory) Class.forName(factory).newInstance();
        } 
        catch (Exception e) {
            throw new SQLException("error msg");
        }
     }
     public abstract AireBatchDao getAireBatchDao() throws SQLException;}

我想了解两者之间的具体区别

return (AireBatchDaoFactory) Class.forName(factory).newInstance();

return new AireBatchDaoFactory();

【问题讨论】:

  • 它叫做反射。有一个String 描述了class 的FQCN,代码尝试首先将String 转换为Class 实例,然后再转换为class 的新实例,假设默认构造函数。这是从String 创建实例的唯一方法 - new 显然在这里不起作用。
  • 只写return class.forName(factory).newInstance(); 可能是通用的,其余的似乎相同。还捕获超级Exception 并抛出SQLException 而不是ClassNotFoundException,也许有人会在一段时间内改变这一点。甚至没有使用变量ORACLE_FACTORY
  • 此外,通常这些工厂是无法实例化的 抽象类 (在您的情况下也是如此)。强制转换是实现方法签名所必需的(也可能失败)。
  • @ankur-singhal 它是public static final - 意图大概是传递给 API 的默认值,异常处理对于 JDBC 代码来说是非常正常的 - 重新抛出 SQLException 使处理使用 JDBC API 时代码一致。

标签: java reflection factory


【解决方案1】:

不同之处在于new AireBatchDaoFactory 将创建一个特定类 的实例(或者不编译,如果AireBatchDaoFactory 是一个接口或抽象类)。但是factory 可能包含实现AireBatchDaoFactory(如果它是一个接口)或子类(如果它是一个类)的any 类的完全限定名称。 Class.forName(factory).newInstance() 将加载 factory 命名的类并创建它的新实例。

对于代码库的运行时插件来说,这是一种非常常见的模式。通常,您定义插件实现的接口。然后,您提供一种运行时方法来指定要使用的特定类(在属性文件、其他配置等中),并使用Class.forName 加载该类并使用newInstance 创建它的一个实例。由于类是在运行时决定的,它可能是使用它的代码的作者从未见过的东西,在使用它的代码很久之后才编写。因此将其用于插件。

考虑:

Plugin.java:

interface Plugin {
    void doSomething();
}

Foo.java:

public class Foo implements Plugin {
    public void doSomething() {
        System.out.println("I'm Foo");
    }
}

Bar.java:

public class Bar implements Plugin {
    public void doSomething() {
        System.out.println("I'm Bar");
    }
}

App.java:

public class App {

    public static final void main(String[] args) {
        try {
            String classToLoad = args[0];
            Plugin plugin = Class.forName(classToLoad).newInstance();
            plugin.doSomething();
        }
        catch (Exception e) {
            //...handle error...
        }
    }
}

跑步

java App Foo

...加载Foo 类并在其上调用doSomething,给我们"I'm Foo"

跑步

java应用栏

...加载Bar 类并在其上调用doSomething,给我们"I'm Bar"

【讨论】:

    【解决方案2】:

    仔细看方法

    getFactory(String factory)
    

    您正在接收类名作为参数。你不能写

    factory factory = new factory(); // error.
    

    在哪里

     Class.forName(factory).newInstance();
    

    有效。

    简而言之,您正在使用在编译时不使用的名称为类创建实例,并在运行时使用反射创建具有传递名称的实例。

    【讨论】:

      【解决方案3】:

      您不能为抽象类调用new AireBatchDaoFactory()。在您的示例中,反射用于获取具有名称的类的实例,该名称作为 getFactory 方法的参数传递。此类应扩展 AireBatchDaoFactory 抽象类。

      在这种情况下,您可以更改在运行时从中获得的 AireBatchDaoFactory 实现。要知道为什么会这样,你可以看看这个逻辑是如何使用的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-25
        • 2012-03-14
        • 2020-01-18
        • 2021-11-02
        • 1970-01-01
        相关资源
        最近更新 更多