2

Я получаю следующую ошибку от метода, в котором мне нужен общий Возвращаемый тип:Ошибка несоответствия типа в методе с общим возвратом (Почему мне нужно делать приведение к родовому типу в этом методе, но не в похожих?)

Description Resource Path Location Type 
Type mismatch: cannot convert from PageTypeOne to P SecuredPage.java 

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

Основная структура проекта заключается в следующем:

Базовый класс Страница незащищенные веб-страниц
Базовый класс SecuredPage для всех защищенных веб-страниц

У меня был подобный вопрос ранее сегодня, который был с помощью сообщества переполнения стека: Bound mismatch error and java generic method.

У меня теперь есть другая проблема с аналогичным методом.

Класс базовой страницы ж/вспомогательный метод, который строит страницы заключается в следующем:

public abstract class Page<T extends Page<T>> extends SlowLoadableComponent<T> { 

    protected static final <T extends Page<T>> T constructPage(WebDriver driver, 
    int timeoutInSeconds, java.lang.Class<T> pageClass) 
    { 
     Page<T> p = null; 

     try { 
      Constructor<T> pageConstructor = pageClass.getConstructor(
       WebDriver.class, String.class, Integer.TYPE); 
      p = pageConstructor.newInstance(driver, driver.getCurrentUrl(), 
       timeoutInSeconds); 
      p.get(); 

     } catch(Exception e) { 

     } 

     return pageClass.cast(p);  
    } 
} 

SecuredPage класс следующим образом:

public class SecuredPage<T extends SecuredPage<T>> extends Page<T> { 

    ..... 
} 

Это метод, который я пытаюсь реализовать внутри SecuredPage - Я хочу иметь единственный метод, который возвращает оба типа страниц, которые возникают в результате открытия ссылки в методе в моем веб-приложении:

public final <P extends SecuredPage<P>> P loadContext(final String context) throws 
NoSuchElementException { 
    Menu m = pageHeader.getMenu(); 
    WebElement link = m.clickLink(context); 
    String linkHref = link.getAttribute("href"); 

    if (linkHref.contains("somematchtext")) { 
      return Page.constructPage(getDriver(), getTimeoutInSeconds(), 
       PageTypeOne.class); <==== This is where I get the error 
    } else if (linkHref.contains("someothermatchtext")) { 
      return Page.constructPage(getDriver(), getTimeoutInSeconds(), 
       PageTypeTwo.class); <==== This is wheere I get the error 
    } 
} 

PageTypeOne и PageTypeTwo как продлить SecuredPage:

public final class PageTypeOne extends SecuredPage<PageTypeOne> { 
    ..... 
} 


public final class PageTypeTwo extends SecuredPage<PageTypeTwo> { 
    ..... 
} 

У меня есть подобный метод в моем классе LoginPage, который возвращает LoginPage, если кто-то пытается войти в систему с неверной идентификационной информацией, и HomePage (который проходит SecuredPage), если они регистрируются с действительными учетными данными. Я не получаю эту ошибку с этим методом. И я не получаю сообщение об ошибке с помощью метода constructPage в моем классе страницы, так что я не понимаю, почему я получаю эту ошибку с моим методом loadContext в SecuredPage:

public final class LoginPage extends Page<LoginPage> { 

    public final HomePage loginWithGoodCredentials(final User user) { 
     return login(user, HomePage.class); 
    } 

    public final LoginPage loginWithBadCredentials(final User user) { 
     return login(user, LoginPage.class); 
    } 

    public final <T extends Page<T>> T login(final User user, final Class<T>  
     expectedPage) { 
     enterUsername(user.getUsername()); 
     enterPassword(user.getPassword()); 
     loginButton.click(); 

     return Page.constructPage(getDriver(), getTimeoutInSeconds(), 
      expectedPage); 
    } 
} 

ответ

2

Обычно компилятор логически вывести метод универсального типа (P в вашем случае) из метода подпись (loadContext в вашем случае). Но метод loadContext не использует P по параметрам, которые помогают компилятору вывести P.

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

Представьте себе, что кто-то указан общий параметр при вызове метода:

securedpageInstance.<SomeCompliantClassWithPBounds>loadContext(...) 

SomeCompliantClassWithPBounds может отличаться от PageTypeOne.

Ваш второй пример работает, потому что у вас есть Class<T> на login метод подписи. Компилятор знает, кто такой T, и он уверен, что тип возврата будет таким же.

+0

Спасибо. Это ясно объясняет разницу. – Selena

1

Я согласен с ответом dcernahoschi.

Чтобы продолжить, вы можете определить loadContext как public final SecuredPage<? extends SecuredPage<?>> loadContext(...) и работать с «неизвестным» подклассом SecuredPage отсюда.
Вы также можете изменить код в loadContext к return (P) Page.constructPage(...); и позже, «трюк» компилятор в позволяя что-то вроде PageTypeOne page = loadContext(...);

Я настоятельно рекомендуем использовать первый подход, поскольку вызывающий код не должен знать исход loadContext() , Если, с другой стороны, вызывающий код ожидает, что определенная страница будет загружена, она должна пройти по классу страницы (и вы избавитесь от if/else в loadContext), как это делает ваш логин.

+0

Спасибо. Я решил пойти с подстановочным знаком. Это прекрасно работает для моих целей, и у меня нет никаких предупреждений компилятора. – Selena