Почему расширение интерфейса не позволяет возвращать более точные типы? - PullRequest
3 голосов
/ 15 марта 2011

Например, если у нас есть класс Person, а класс Student расширяет Person. Мы можем иметь:

public interface PersonQueryBuilder<T extends Person> {
    PersonQueryBuilder<T> withName(String name);

    PersonQueryBuilder<T> withAgeBetween(int from, int to);

    List<T> getResultList();
}

public interface StudentRepository<T extends Student> extends PersonQueryBuilder<T> {
    StudentRepository studying(Course course);
}

Так почему же, когда у меня есть StudentRepository, методы withName () и withAgeBetween () возвращают PersonQueryBuilder, а не StudentRepository? Это очень раздражает. Есть ли какой-нибудь «изящный» способ решения этой проблемы?

Ответы [ 3 ]

4 голосов
/ 15 марта 2011
interface StudentRepository<T extends Student> extends PersonQueryBuilder<T> {
   StudentRepository<T> studying(Course course);
   StudentRepository<T> withName(String name);
   StudentRepository<T> withAgeBetween(int from, int to);
}

Вы можете переопределить методы в подчиненном интерфейсе. Разработчики StudentRepository теперь должны будут возвращать StudentRepository для этих методов, но реализацию все же можно полиморфно использовать как PersonQueryBuilder.

3 голосов
/ 16 марта 2011

Это довольно забавно.Я предлагаю следующее (уродливое?) Решение, которое не требует от вас переопределения методов интерфейса, как это делает ответ Марка:

interface PersonQueryBuilder<T extends Person, U extends PersonQueryBuilder<T,U>> {
    U withName(String name);

    U withAgeBetween(int from, int to);
}

interface StudentRepository extends PersonQueryBuilder<Student, StudentRepository> {
    StudentRepository studying(Course course);
}

Также вы используете необработанные типы в качестве результатов в ваших текущих реализацияхваши with методы.

2 голосов
/ 16 марта 2011

Для одного, потому что вы используете необработанный тип PersonQueryBuilder в качестве возвращаемого типа.T не отображается в этом типе, так почему конкретный T имеет значение?Поэтому первое улучшение:

PersonQueryBuilder<T> withName(String name);

PersonQueryBuilder<T> withAgeBetween(int from, int to);

Следующая проблема связана с тем, что StudentRepository не обязательно должен быть единственным PersonQueryBuilder для студентов, поскольку было бы совершенно законно объявить:

interface UniversityRepository<T extends Student> extends PersonQueryBuilder<T> { }

Итак, по вашей логике, тип возвращаемого значения должен быть StudentRepository или UniversityRepository?Или как-то оба?Компилятор не может знать ваше намерение, поэтому должен указать, что вы хотите, например, используя ковариантный тип возвращаемого значения:

public interface StudentRepository<T extends Student> extends PersonQueryBuilder<T> {
    @Override
    StudentRepository<T> withName(String name);

    @Override
    StudentRepository<T> withAgeBetween(int from, int to);

    StudentRepository<T> studying(Course course);
}

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

public interface PersonQueryBuilder<T extends Person, R extends PersonQueryBuilder<T, R>> {
    R withName(String name);

    R withAgeBetween(int from, int to);

    List<T> getResultList();
}

и

public interface StudentRepository<T extends Student, R extends StudentRepository<T, R> extends PersonQueryBuilder<T, R> {
    R studying(Course course);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...