在Java中将基类强制转换为派生类

在Java中将基类强制转换为派生类,java,inheritance,Java,Inheritance,目前在我的Java程序中,我有一个基本的父类,它包含几个方法,这些方法返回它自己的一个新实例。例如: public BasePage fill(String input, By locator) { driver.findElement(locator).clear(); driver.findElement(locator).sendKeys(input); return new BasePage(driver); } 还有,我有几个类用它们自己的方法扩展这个基类。

目前在我的Java程序中,我有一个基本的父类,它包含几个方法,这些方法返回它自己的一个新实例。例如:

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

    return new BasePage(driver);
}
还有,我有几个类用它们自己的方法扩展这个基类。我的问题是:在我的主程序中,有没有一种方法可以调用这些父方法,但不使用强制类型转换将返回值分配给子类?例如,如果我有一个
loginPage
子类,它需要在主程序中使用此方法,我将如何将其转换为:

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

loginPage = loginPage.fill("username", usernameLocator);
我在其中初始化类:

public class LoginPage extends BasePage {
}

虽然本例中未提及,但是否可以允许它返回任何子类,这样它就不限于一个类。

是的,有一种方法。在子类(例如
LoginPage
)中,重写
fill
方法。使用Java(滚动到底部),可以指定
LoginPage
作为返回类型

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

    return new LoginPage(driver);
}
这假设您可以使用
驱动程序创建
登录页面
,假设该驱动程序属于
驱动程序
类,这意味着您需要一个接受
驱动程序
的子类构造函数

public LoginPage(Driver driver)
{
    super(driver);
    // Anything else LoginPage-specific goes here
}

不,不是这样的。如果
BasePage
是调用给定
fill()
方法的引用的形式类型,则返回值的形式类型为
BasePage
。如果没有强制转换,则不能将该值视为
BasePage
的子类型

在您的特定示例中,即使cast也会因
ClassCastException
而失败,因为基类的方法(子类未显示要覆盖该方法)返回一个类为
BasePage
的对象,而该对象不是
LoginPage
的实例

但是,子类可以使用协变返回类型覆盖该方法:

public class LoginPage extends BasePage {
    @Override
    public LoginPage fill(String input, Locator by) {
        return new LoginPage(/* ... */);
    }
}
在这种情况下,如果在引用
LoginPage
(或其子类之一)时调用该方法,则返回值是
LoginPage
的一个实例,可以这样处理:

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

这里有几种方法可以实现,但是如果新实例与旧的几乎相同,可能有一些修改,我认为您应该考虑使层次结构克隆能够。p>

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);
      } 
    }
...
}
然后,子类需要使用适当的强制类型替换父类的填充

public class LoginPage extends BasePage {

    @Override
    public LoginPage fill(String input, By locator) {
        return (LoginPage) super.fill(input,locator);
    }
}
请注意,克隆将始终返回原始调用实例的实例。。。在本例中,登录页面

根据层次结构中的其他成员字段以及这些字段在克隆之间的共享方式,此方法可能不起作用或需要改进

只要保持所有子类(在本例中为驱动程序)构造函数的签名不变,另一种方法就是保持方法的逻辑填充在父类中,并通过传递类对象选择适当的子类

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);
    } 
  }
}

您可以重载填充子类以避免传递该类,但这样我们的工作将与上面的克隆解决方案一样多。

您熟悉吗?我已经阅读了关于Java多态性的页面,但是它没有关于返回父类并在不强制转换的情况下将该值赋给子类的任何信息。这里唯一的问题是,要获得该值,每个子类必须通过提供超类实现的精确副本来重写方法,除了newthing()之外我希望有一种方法可以让我返回BasePage类的任何子类。不过,这对我的情况确实有帮助。关于代码的最后一部分,方法
newInstance()
似乎不接受任何参数。这应该留空并且构造函数被继承,还是我需要从包中导入此方法以获得您使用的方法?@bagelmakers抱歉我的错误,因为我是盲编码的(没有检查API)。。。必须使用getConstructor(Class…x)获取相应的构造函数,并使用相应的参数调用newInstance。我改变了答案以反映这一点。
 LoginPage lp = page.fill(input,loc,LoginPage.class);