Java 使用抽象类型强制转换时使用反射

Java 使用抽象类型强制转换时使用反射,java,selenium-webdriver,webdrivermanager-java,Java,Selenium Webdriver,Webdrivermanager Java,我完全支持有人为这个问题推荐一个更好的标题。我也非常愿意简化我描述问题的方式 上下文:我有一个自动化设置,允许通过属性文件配置浏览器。因此,如果有人在该文件中有browser=chrome,那么应该实例化的特定WebDriver实例就是ChromeDriver 我还使用它,您可以下载特定WebDriver类型的二进制文件。所以在本例中,我只想下载属性文件中的任何浏览器驱动程序。如果是Chrome,我想使用ChromeDriverManager 当然,这里的关键是,我必须概括所有这些,因为我不知道

我完全支持有人为这个问题推荐一个更好的标题。我也非常愿意简化我描述问题的方式

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

我还使用它,您可以下载特定WebDriver类型的二进制文件。所以在本例中,我只想下载属性文件中的任何浏览器驱动程序。如果是Chrome,我想使用ChromeDriverManager

当然,这里的关键是,我必须概括所有这些,因为我不知道有人会用什么。但是为了解决我的问题,为了说明问题,让我们继续讨论这些运动部件:chrome、ChromeDriver、ChromeDriver Manager

代码:

我有一个driverMap,它保存与浏览器名称关联的WebDriver类的实例

private static final Map<String, Class<?>> driverMap = new HashMap<String, Class<?>>() {
    {
        put("chrome", ChromeDriver.class);
        put("firefox", FirefoxDriver.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);
}
但我必须使用反射,因为我不知道我将使用哪个经理。因此,理想情况下,我需要它,以便我可以做到这一点:

manager.getInstance().setup();
manager.getInstance().setup();
我不知道为了让经理工作,我能做些什么。或者,我不知道一旦我确定了某个特定的类是什么,我是否可以将其转换为该类

我可以完全放弃使用WebDriverManager,但这是一个很好的解决方案,我希望找到一些方法来满足我的需要

所以我不知道如何达到我想要的效果。具体来说 我想要的效果是打一个与此等效的电话:

ChromeDriverManager.getInstance().setup();
ChromeDriverManager.getInstance().setup();
但我必须用反射来做,因为我不知道是什么 经理,我会用的。因此,理想情况下,我需要它,以便我可以做到这一点:

manager.getInstance().setup();
manager.getInstance().setup();
我不知道为了让经理工作,我能做些什么。或 我不知道一旦我下定决心,我是否能投到一个特定的班级 那是什么课

经过调查,我发现ChromeDriverManager.getInstance是一个静态方法。静态方法是在编译时绑定的,而不是在运行时绑定的,所以如果在编译时不知道要调用哪个类的方法,则不能通过普通的方法调用表达式调用该方法。关键是你不知道

但这是愚蠢的。该方法的要点是提供类的一个实例,作为指定的特殊实例在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在结果对象上声明的所有实例方法。特别是,您可以调用安装程序,如图所示


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

通过John的评论和帮助,了解这种方法是否有意义,我确实找到了一种稍微粗暴的方法来处理此问题,即:

Class<?> driverBinary = driverManager.get(driverClass);

if (driverBinary.newInstance() instanceof ChromeDriverManager) {
    ChromeDriverManager.getInstance().setup();
}
我之所以说暴力,是因为我意识到这个解决方案并没有提供太多的技巧。我还需要玩弄约翰提供的解决方案

这些类型的挑战在测试框架中经常出现,您不知道测试将在什么条件下运行。因此,能够制定更好或更坏的方法来做这些事情似乎是有用的


以上内容目前显示在我的类中。

WebDriverManager针对不同的浏览器有不同的DriverManager,即Chrome的ChromeDriverManager、Firefox的FirefoxDriverManager等等。此外,它还有一个可以参数化的通用driverManager。此驱动程序直接命名为WebDriverManager。getInstance of driver方法接受要使用的底层浏览器的WebDriver类,即ChromeDriver、FirefoxDriver等:

你可以找到一份工作
示例一个JUnit 4参数化测试,使用具有相同测试逻辑的Chrome和Firefox,其中通用driverManager为Chrome和Firefox解析正确的二进制文件

您抛出了很多单词,但没有说明具体问题。你标记的那条线到底出了什么问题?它是否生成编译错误?什么错误?它是否在运行时抛出异常?什么例外?说得好,约翰伯林格。问题在于我要使用的行:manager.getInstance.setup;我不能这样做,因为我需要manager是特定浏览器二进制ChromeDriverManager或FirefoxDriverManager的实例。它们都使用BrowserManager作为它们的父级,这是抽象的。我很难描述这一点,这就是为什么我将cast示例包含在WebDriver中的原因。因此,与其说我遇到了错误,还不如说我根本无法使用代码行管理器.getInstance.setup。这是因为我显然不能像对待WebDriver那样,使用更通用的实例。我不理解你所说的使用casting的问题。给定类C的对象O,可以将对O的任何引用强制转换为C及其所有超类型(包括抽象类型)中的任何类型。您正在强制转换的特定引用的声明类型不限制此限制,尽管它可能允许编译器检查强制转换的合理性。@约翰伯林格:问题是驱动程序使用driverClass ChromeDriver并强制转换到WebDriver。这允许我在驱动程序上调用WebDriver方法。我不能对经理做同样的事。它使用driverBinary ChromeDriverManager,但是。。。我不知道该放弃什么。BrowserManager是编译的,但我不能像使用驱动程序那样调用manager上的方法——我认为这是因为BrowserManager是抽象的,而WebDriver是一个接口。所以我不知道在经理的情况下该投什么票。。。或者我是否应该在这样的背景下出演。很有趣!谢谢你提供的信息。我会玩玩的。我必须说,我真的很喜欢这个,因为我试过了。我喜欢它不需要我硬编码任何特定的驱动程序管理器。我还认为这个答案完全符合我问题的原始重点,这可能会帮助其他人探索类似的想法。获取driverBinary类的实例只是为了测试它的类是什么,这同样是愚蠢的。如果您想沿着这些路线继续学习,那么更好的条件是如果driverBinary==ChromeDriverManager.class而不使用driverBinary.newInstance。当然,这需要你为你想要支持的每一个BrowserManager类硬编码一个案例。值得注意的是,如果你不提及人们认为愚蠢的事情,你的表现通常会好得多。通常有更好的方式来评论事物。尽管如此,我们还是非常感谢您的帮助,并让我更多地思考如何处理这一问题。
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

import io.github.bonigarcia.wdm.WebDriverManager;

// ...

Class<? extends WebDriver> driverClass = ChromeDriver.class;

// ... other option:
// driverClass = FirefoxDriver.class;

WebDriverManager.getInstance(driverClass).setup();
WebDriver driver = driverClass.newInstance();