java - 使用抽象类型进行转换时使用反射

标签 java selenium-webdriver webdrivermanager-java

我完全赞成有人为这个特定问题推荐一个更好的标题。我也非常愿意努力简化我描述问题的方式。

上下文:我有一个自动化设置,允许通过属性文件配置浏览器。因此,如果有人在该文件中有“browser=chrome”,那么具体的 WebDriver应该实例化的实例是 ChromeDriver .

我也在使用 WebDriverManager您可以在其中下载特定 WebDriver 的二进制文件类型。所以在这种情况下,我只想下载该属性文件中的任何浏览器驱动程序。所以如果那是 Chrome,我想使用 ChromeDriverManager .

当然,这里的关键是我必须概括所有这些,因为我不知道有人会使用什么。但出于我的问题的目的,为了说明问题,让我们坚持使用这些移动部件:“chrome”,ChromeDriver , ChromeDriverManager .

代码:

我有一个 driverMap包含 WebDriver 的实例与浏览器名称关联的类。

private static final Map<String, Class<?>> driverMap = new HashMap<String, Class<?>>() {
    {
        put("chrome", ChromeDriver.class);
        put("firefox", FirefoxDriver.class);
    }
};

我还有一个 driverManager关联 BrowserManager具有特定 WebDriver 的类类(class)。
private static final Map<Class<?>, Class<?>> driverManager = new HashMap<Class<?>, Class<?>>() {
    {
        put(ChromeDriver.class, ChromeDriverManager.class);
        put(FirefoxDriver.class, FirefoxDriverManager.class);
    }
};

只是为了更多的上下文,所有这些都在一个名为 Driver 的类中。它开始是这样的:
public final class Driver {
    private static WebDriver driver;
    private static BrowserManager manager;
   ....
}

这两个变量在这里与下一位相关。一个 add方法被调用以将特定的浏览器配置添加到测试中。所以这里是那个方法,它显示了当浏览器添加到混合中时如何使用上述方法:
public static void add(String browser, Capabilities capabilities) throws Exception {
    Class<?> driverClass = driverMap.get(browser);
    Class<?> driverBinary = driverManager.get(driverClass);

    manager = (BrowserManager) driverBinary.getConstructor().newInstance(); /// <<--- PROBLEM

    driver = (WebDriver) driverClass.getConstructor(Capabilities.class).newInstance(capabilities);
}
  • 你可以看到我用driverClass ,这将是这样的:org.openqa.selenium.chrome.ChromeDriver .
  • 你可以看到我用driverBinary ,这将是这样的:io.github.bonigarcia.wdm.ChromeDriverManager .

  • 但我评论了上面我有问题的那一行。

    问题:你可以看到我使用了 driver用于存储 WebDriver 的变量实例和 manager用于存储 BrowserManager 的变量实例。

    以下是我在 driver 的情况下这样做的方式和原因:
  • ChromeDriver extends RemoteWebDriver
  • RemoteWebDriver implements WebDriver
  • interface WebDriver

  • 所以这样做是让我得到更一般的( ChromeDriver )的适当类型( WebDriver )。因此在我的driver变量,我可以将反射调用转换为 WebDriver因此引用 driver好像就是那个实例。

    我不能为 manager 做同样的事情.

    而且我不知道这是否是因为那个特定的 Java 库是如何工作的。具体来说:
  • ChromeDriverManager extends BrowserManager
  • abstract class BrowserManager

  • 所以我不能在 manager 上调用方法好像它是 BrowserManager 的特定类型(如 ChromeDriverManager )我可以为 driver (这是 WebDriver 的特定类型,如 ChromeDriver )。

    这似乎是因为最终 WebDriver是一个接口(interface),但 BrowserManager是抽象的。

    所以我不知道如何达到我想要的效果。具体来说,我想要的效果是调用相当于这个:
    ChromeDriverManager.getInstance().setup();
    

    但是我必须使用反射来做到这一点,因为我不知道我将使用哪个经理。所以理想情况下我想要它,这样我就可以做到这一点:
    manager.getInstance().setup();
    

    我不知道为了制作 manager 我可以放弃什么工作。或者我不知道一旦我确定了那个类是什么,我是否可以转换到一个特定的类。

    我可以完全放弃使用 WebDriverManager,但它是一个很好的解决方案,我希望找到一些方法来做我需要的事情。

    最佳答案

    So I don't know how to achieve the effect I want. Specifically, the effect I want is to make a call equivalent to this:

    ChromeDriverManager.getInstance().setup();
    

    But I have to do that using the reflection since I don't know what manager I'll be using. So ideally I want it so that I can do this:

    manager.getInstance().setup();
    

    I don't know what I can cast down to in order to make manager work. Or I don't know if I can cast to a specific class once I've determined what that class is.



    经调查发现ChromeDriverManager.getInstance()是一种静态方法。静态方法是在编译时绑定(bind)的,而不是在运行时绑定(bind)的,因此如果您在编译时不知道要调用哪个类的方法,则无法通过正常的方法调用表达式调用该方法。关键是你不知道。

    但这是愚蠢的。该方法的重点是提供该类的一个实例,注册为 BrowserManager。作为指定的特例。尝试通过首先获取其他不需要的其他实例来尝试这样做是没有意义的,因为您也不需要类的实例来调用类的静态方法。

    看来具体的 BrowserManager子类实现这样的模式getInstance()方法。尽管它们不是多态的,因此不能保证存在,但您可以依靠模式来定位和反射地调用它们(而不是反射地调用构造函数)。例如,
        Class<?> driverBinary = driverManager.get(driverClass);
    
        try {
            // Retrieves a no-arg method of the specified name, declared by the
            // driverBinary class
            Method getInstanceMethod = driverBinary.getDeclaredMethod("getInstance");
    
            // Invokes the (assumed static) method reflectively
            BrowserManager manager = (BrowserManager) getInstanceMethod.invoke(null);
    
            manager.setup();
        } catch ( IllegalAccessException
                | IllegalArgumentException
                | InvocationTargetException
                | NoSuchMethodException
                | SecurityException e) {
            // handle exception
        }
    

    您可以调用 BrowserManager 声明的所有实例方法。在生成的对象上。特别是,您可以调用 setup() , 如图所示。

    另一方面,如果您不需要将您的实例注册为特殊指定的BrowserManager例如,那么你不需要通过getInstance()一点也不。您已经拥有的获取实例的方法足以获取实例,然后您可以调用它的setup()。直接方法。我不确定是否没有向 BrowserManager 注册实例会出现任何问题。

    关于java - 使用抽象类型进行转换时使用反射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43944184/

    相关文章:

    java - ExpectedException 的原因是什么?

    java - 哪个版本的 FindBugs 可以在 Java 1.4 上运行?

    javascript - Webdriver.IO 接收到 : "npm ERR! Test failed. See above for more details." but can't see the errors

    java - 即使使用 selenium 中的 javascript 方法也无法更改 header 名称

    java - Cucumber Java (Maven) WebdriverManager (Bonigarcia) 无法从属性获取 URL

    java - @Resource 没有在 jboss 7 中注入(inject) SessionContext

    java - 为什么我的方法返回空值?

    java - Junit 5,Spring 应用程序上下文未在 @DirtiesContext 上关闭

    java - 使用 WebDriverManager 时如何设置 Chrome 选项?

    java - Linux IntelliJ Chrome WebDriverManager "chrome (or any other browser) failed to start"