将基础 class 转换为 Java 中的派生 class

Casting a base class onto a derived class in Java

目前在我的 Java 程序中,我有一个基本的 Parent class 和几个方法,其中 return 是它自己的一个新实例。例如:

public BasePage fill(String input, By locator) {
    driver.findElement(locator).clear();
    driver.findElement(locator).sendKeys(input);

    return new BasePage(driver);
}

另外,我有几个 classes 用它们自己的方法扩展了这个基础 class。我的问题是:在我的主程序中是否有一种方法可以调用这些 Parent 方法,但不使用强制转换就将 returned 值分配给子 class?例如,如果我有一个 loginPage 子 class 需要在主程序中使用此方法,我将如何转换:

loginPage = (LoginPage) loginPage.fill("username", usernameLocator);

进入这个:

loginPage = loginPage.fill("username", usernameLocator);

我在其中初始化 class:

public class LoginPage extends BasePage {
}

虽然在此示例中未提及,但是否可以允许它 return 任何子 classes,这样它就不仅限于一个 class。

是的,有办法。在 subclass(例如 LoginPage)中,覆盖 fill 方法。使用 Java 的 covariant return types(滚动到底部),您可以将 LoginPage 指定为 return 类型。

public LoginPage fill(String input, By locator) {
    driver.findElement(locator).clear();
    driver.findElement(locator).sendKeys(input);

    return new LoginPage(driver);
}

假设您可以创建一个 LoginPage 和一个 driver,假设是一个 Driver class,这意味着您需要一个子 [=25] =] 采用 Driver.

的构造函数
public LoginPage(Driver driver)
{
    super(driver);
    // Anything else LoginPage-specific goes here
}

不,不是这样。如果 BasePage 是您在其上调用给定 fill() 方法的引用的正式类型,则 return 值的正式类型为 BasePage。如果没有转换,您不能将该值视为 BasePage 的子类型。

在您的特定示例中,即使是强制转换也应该因 ClassCastException 而失败,因为基础 class 的方法未显示 subclass 覆盖,return 是 class 是 BasePage 的对象,并且这样的对象不是 LoginPage.

的实例

但是,subclass 可以覆盖该方法,使用协变 return 类型:

public class LoginPage extends BasePage {
    @Override
    public LoginPage fill(String input, Locator by) {
        return new LoginPage(/* ... */);
    }
}

在这种情况下,如果您在对 LoginPage(或 its subclasses 之一的引用上调用该方法,则 return 值是 LoginPage 的实例,可以这样处理:

LoginPage page1 = new LoginPage();
LoginPage page2 = page1.fill("username", usernameLocator);

这里有几种方法可以继续,但如果新实例与旧实例几乎相同,可能有一些修改,我认为您应该考虑制作层次结构 clone-able。

public class BasePage implements Cloneable {

    public BasePage fill(String input, By locator) {
        driver.findElement(locator).clear();
        driver.findElement(locator).sendKeys(input);

        return (BasePage) clone();
    }

    @Override
    protected Object clone() {
      try {  
        return super.clone();
      } catch ( CloneNotSupportedException ex) {
        throw new RuntimeException("this can't ever happen!!!",ex);
      } 
    }
...
}

然后 child classes 需要用适当的 cast

覆盖 parent 的填充
public class LoginPage extends BasePage {

    @Override
    public LoginPage fill(String input, By locator) {
        return (LoginPage) super.fill(input,locator);
    }
}

请注意,克隆将始终 return 原始调用实例的实例...在本例中为 LoginPage。

此方法可能行不通或需要改进,具体取决于层次结构中的其他成员字段是什么以及它们应该如何在克隆之间共享。

只要您保持所有 child classes 的构造函数的签名不变,在这种情况下(驱动程序),一种不同的方法就是保持方法的逻辑通过传递 class object...

填写 parent 和 select 适当的 child class
public class BasePage {

  public <T extends BasePage> T fill(String input, By locator, Class<T> clazz) {
    driver.findElement(locator).clear();
    driver.findElement(locator).sendKeys(input);
    // not sure if the cast is even needed.
    try {
      return (T) clazz.getConstructor(Driver.class).newInstance(driver);
    } catch (Exception ex) {
      throw new RuntimeException("whoops! something went wrong",ex);
    } 
  }
}

然后获取登录页面...

 LoginPage lp = page.fill(input,loc,LoginPage.class);

您可以重载填写 children classes 以避免传递 class 但这样我们将做与上面的克隆解决方案一样多的工作。