使用抽象类型变量中的具体 class

Using concrete class from a abstract type variable

如果这个问题已经被问到,我很抱歉,我还没有找到类似我的问题的东西...

我 working/playing/learning 建立某种测试环境...在其中,我正在构建一个应用层(一个 classes 的包,它是应用程序的不同 pages/windows/forms)。简化设置如下:

public abstract class WebPage {

    protected WebDriver driver;

    protected WebElement getElement(By by){
        WebElement element = (new WebDriverWait(driver, 10))
              .until(ExpectedConditions.presenceOfElementLocated(by));

    return element;
}

    public void menuLogout(){
        System.out.println("Logged out");
    }
}

public class HomePage extends WebPage {

    public ProfilePage ClickLinktoProfilePage(){
        return new ProfilePage();
    }

    public DashBoardPage clickViewDashboard(){
        return new DashBoardPage();
    }

    public String getTitle(){
        return getElement(By.id("title")).getText();
    }
}

public class ProfilePage extends WebPage {

    public String getUsername(){
        return getElement(By.id("name")).getText();
    }

    public String getEmail(){
    return getElement(By.id("email")).getText();
}       
    public HomePage clickReturnToHomePage(){
        return new HomePage();
    }

}

public class DashBoardPage extends WebPage {

    public String getcurrentPeriod(){
        return getElement(By.id("name")).getText();

}
}

这背后的想法是我希望我的测试只包含一个当前网页。我不希望每次更改页面时都创建一个新变量。

我也不想被迫提前知道我要进入哪个页面。我要应用层给我应用的流程。与单击 link 时将您带到下一页的方式相同,我希望当我单击将我带到另一页的 link 时,该方法会告诉我我在哪个页面前往。

(WebPage 抽象 class 还公开了所有具体 WebPage 之间的许多共享方法)

所以我的预期用途是:

WebPage currentPage = new HomePage();

currentPage = currentPage.ClickLinktoProfilePage(); //currentPage = new ProfilePage();
System.out.println(currentPage.getUsername());
currentPage.menuLogout();

遗憾的是,这不起作用,因为 currentPage 变量的类型是 WebPage,它看不到任何具体的 classes 方法。我觉得它既合乎逻辑又奇怪,因为我可以问 "currentPage.getClass().getName();" 并且它会 return "packageName.ConcreteClassName".

要使类型转换正常工作,我需要重新定义变量的类型...(不确定是否可行,甚至是否可行)。

所以我知道我可以在变量中找到 class 的名称,但我不确定从那里去哪里。

有人有解决办法吗?

澄清一下 Radiodef 和我在此处的评论中所说的内容:

你想要的是定义WebPage(你的抽象API),这样你的具体子class就不需要 有 public 不属于 API 的方法。

例如对比标准库中的java.util.List接口。这个接口有多种实现(ArrayListLinkedList 是最著名的,但还有很多其他的),但是大多数使用 List 的代码并不需要关心它是否实际使用 ArrayListLinkedList 或其他东西,因为您需要的所有操作都通过 List 接口公开。

您可以用 WebPage class 做同样的事情。例如,您可以为可以对网页执行的不同操作定义一系列 "hooks":

public abstract class WebPage {
  // methods that each subclass needs to implement
  protected abstract String renderBodyHtml();
  public abstract String getNameToLinkTo();

  // other methods that are common to every page
  public final void serve(
      HttpServletRequest request, HttpServletResponse response) {
     // write the response, using the specific page's body HTML
     response.getWriter().println(renderToBodyHtml());
  }
}

然后您的页面将像这样实施 合同

// Note: the class doesn't need to be public, since anybody that uses
// it can just declare their variable as type WebPage
class Page1 extends WebPage {
  @Override protected String renderBodyHtml() {
    return "<body>Hello world!</body>";
  }

  @Override public String getNameToLinkTo() {
     return "Page1";
  }
}

然后想要使用 WebPage 的代码不需要知道它是 Page1(或任何其他页面):

public static void printPageName(WebPage webPage) {
  System.out.println(webPage.getNameToLinkTo());
}

或者,如 resueman 所说,您可以直接使用 Page1Page2 等类型,使用 WebPage 仅用于实现继承,而不是 API.这也很好——正确的解决方案取决于您希望代码的灵活性(和复杂性)。