为什么扩展接口并不能使它返回更精确的类型?

时间:2011-03-15 20:50:20

标签: java inheritance interface

例如,如果我们有一个类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 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:3)

这很有趣。我提出以下(丑陋的?)解决方案,不要求你重新定义接口中的方法,如Mark的答案那样:

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 :(得分:2)

首先,因为您使用原始类型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);
}