开发者

Why extending an Interface does not make it's method to return more precise types?

For example if we have a class Person and a class Student extends Person. The we may have:

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

    PersonQueryBuil开发者_开发技巧der<T> withAgeBetween(int from, int to);

    List<T> getResultList();
}

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

So why when I have an StudentRepository, both the withName() and withAgeBetween() methods return a PersonQueryBuilder and not and StudentRepository? This is very annoying. Is there any "elegant" way of solving this problem?


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

You can redeclare the methods in the sub-interface. Implementors of StudentRepository will now be required to return a StudentRepository for those methods, but the implementation can still be used polymorphically as a PersonQueryBuilder.


This is kind of funny. I propose the following (ugly?) solution that doesn't require you to redefine the methods in the interface as Mark's answer does:

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

Also you're using raw types as results in your current implementations of your with methods.


For one, because you use the raw type PersonQueryBuilder as return type. T does not appear in this type, so why should a particular T matter? A first improvement therefore is:

PersonQueryBuilder<T> withName(String name);

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

The next problem stems from that a StudentRepository need not be the only PersonQueryBuilder for Students, as it would be entirely legal to declare:

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

So by your logic, should the return type be StudentRepository or UniversityRepository? Or somehow both? The compiler can't know your intention, so must specify what you want, for instance by using a covariant return type:

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

The obvious disadvantage of that approach is that you have to repeat all inherited methods. You can get around this with a type parameter for the actual repository:

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

    R withAgeBetween(int from, int to);

    List<T> getResultList();
}

and

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜