2015-01-28 5 views
3

В настоящее время в моей программе Java у меня есть базовый класс родителя с несколькими методами, которые возвращают новый экземпляр самого себя. Например:Кастинг базового класса на производный класс в 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 { 
} 

Хотя не упоминается в этом примере, можно разрешить ему вернуться любого из дочерних классов, таким образом, что оно не ограничивается только один класс.

+1

Вы знакомы с ['Java Polymorphism'] (http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html)? –

+0

Я прочитал страницу о полиморфизме Java, но у нее нет никакой информации о возврате родительского класса и присвоении этого значения дочернему классу без литья – bagelmakers

ответ

3

Да, есть способ. В подклассе (например, LoginPage) переопределить метод fill. С помощью Java covariant return types (прокрутите вниз) вы можете указать LoginPage в качестве типа возврата.

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

    return new LoginPage(driver); 
} 

Это предполагает, что вы можете создать LoginPage с driver, предполагается из Driver класса, что означает, что вам нужен конструктор подкласса, который принимает Driver.

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

. Единственная проблема заключается в том, что для этого каждый подкласс должен был бы переопределить путем предоставления точной копии реализации суперкласса, за исключением нового бита Thing(). –

+0

Я надеялся, что будет подход, который позволит мне вернуть любого из детей класса BasePage. Однако это помогает в моей ситуации. – bagelmakers

0

Нет, не такой. Если BasePage является формальным типом ссылки, на которую вы вызываете данный метод fill(), формальный тип возвращаемого значения равен BasePage. Вы не можете рассматривать это значение как подтип BasePage без актерского состава.

В вашем конкретном примере, даже бросок должна неудача с 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); 
1

там несколько способов продолжиться, но если новый экземпляр, если почти так же, как и старый ш возможно, некоторые изменения, я думаю, вы должны подумать о том, чтобы сделать вашу иерархию доступной клонов.

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

Обратите внимание, что клонировать всегда будет возвращать экземпляр исходного ссылающегося экземпляра ... в этом случае LoginPage.

Этот подход может не работать или нуждаться в уточнении в зависимости от того, какие поля других членов в иерархии и как они должны использоваться совместно с клонами.

Другой подход, который работает до тех пор, пока вы сохраняете постоянную подпись конструктора всех дочерних классов, в данном случае (Драйвер), заключается в том, чтобы логика метода заполнила родительский элемент и выбрала соответствующий дочерний класс пропускание объекта класса ...

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 ...

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

Вы можете перегрузить заполнить ребенок классов, чтобы избежать прохождений класса, но тогда мы будем делать, как так же, как и с решением клона выше.

+0

Что касается вашего последнего раздела кода, похоже, что метод 'newInstance()' не принимает никаких аргументов. Если это оставить пустым и конструктор наследуется или мне нужно импортировать этот метод из пакета, чтобы получить тот, который вы использовали? – bagelmakers

+0

@bagelmakers жалеют мою ошибку, поскольку я кодировал слепые (без проверки API) ... вам нужно получить соответствующий конструктор с getConstructor (Class ... x) и вызвать newInstance с соответствующими аргументами. Я изменил ответ, чтобы отразить это. –

Смежные вопросы