我完全赞成有人为这个特定问题推荐一个更好的标题。我也非常愿意努力简化我描述问题的方式。
上下文:我有一个自动化设置,允许通过属性文件配置浏览器。因此,如果有人在该文件中有“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
)的适当类型( WebDriver
)。因此在我的driver
变量,我可以将反射调用转换为 WebDriver
因此引用 driver
好像就是那个实例。我不能为
manager
做同样的事情. 而且我不知道这是否是因为那个特定的 Java 库是如何工作的。具体来说:
所以我不能在
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/