As Plínio Pantaleão said (or actually, wanted to say) in the comment, you can define the interface to return a List<? extends Student>
import java.util.List;
class Attributes {}
class Student { }
interface A {
Student isCurrentStudent(String name);
List<? extends Student> getStudentList();
}
interface B extends A {
List<? extends Student> getColleges();
}
class SeniorStudent extends Student{ }
interface C extends A {
@Override
SeniorStudent isCurrentStudent(String name);
@Override
List<SeniorStudent> getStudentList();
void addStudent(Attributes s);
}
interface D extends B,C {
@Override
List<SeniorStudent> getColleges();
}
public class CovariantList
{
public static void main(String[] args)
{
B b = null;
List<? extends Student> collegesB = b.getColleges();
D d = null;
List<SeniorStudent> collegesD = d.getColleges();
}
}
If you only know the most "basic" interface (B, in this case) you can only obtain a List<? extends Student>
. If you know the particular type (as in D), you can obtain the covariant type List<SeniorStudent>
.
EDIT Update based on the comments and the edited question:
The requirement that the API may be used as in the edited section of the main question could probably be achieved with something like this:
import java.util.List;
public class StudentsReturnTypesTest
{
public static void main(String[] args)
{
B bObject = getBObject();
List<Student> studentList = bObject.getColleges();
D dObject = getDObject();
List<SeniorStudent> seniorStudentList = dObject.getColleges();
}
private static D getDObject() { return null; }
private static B getBObject() { return null; }
}
interface A<T extends Student> {
public Student isCurrentStudent(String name);
public List<T> getStudentList();
}
interface AS extends A<Student> { }
interface B extends AS {
public List<Student> getColleges();
}
interface C extends A<SeniorStudent> {
public SeniorStudent isCurrentStudent(String name);
public List<SeniorStudent> getStudentList();
public void addStudent(Attributes s);
}
interface D extends C /* B Not possible, see below */ {
public List<SeniorStudent> getColleges();
}
interface Attributes {}
interface Student {}
interface SeniorStudent extends Student {}
However in one of the comments, you mentioned
"For the interface D I wanted to inherit methods from B too"
This will be difficult. You basically have the problem that the method getColleagues()
will then be inherited with different return types: Once with List<Student>
and once with List<SeniorStudent>
. It's simply not type-safe to do this.
At the moment, the only method that is defined in interface B is getColleagues()
. And since this method (with a different return type) is already inherited from interface C, I assume that the intention to "...inherit methods from B too" referred to methods that are currently not contained in the example code that you posted.
A solution for this could possibly be to extract that the "methods from B" into a dedicated interface. The key point here is to avoid inheriting a method twice with different return types. This could possibly be achieved like in this example, where the respective methods are summarized in an interface with the beautiful name AdditionalMethodsThatHaveBeenInB
...
import java.util.List;
public class StudentsReturnTypesTest
{
public static void main(String[] args)
{
B bObject = getBObject();
List<Student> studentList = bObject.getColleges();
bObject.methodThatWasInB();
D dObject = getDObject();
List<SeniorStudent> seniorStudentList = dObject.getColleges();
dObject.methodThatWasInB();
}
private static D getDObject() { return null; }
private static B getBObject() { return null; }
}
interface A<T extends Student> {
public Student isCurrentStudent(String name);
public List<T> getStudentList();
}
interface AS extends A<Student> { }
interface B extends AS, AdditionalMethodsThatHaveBeenInB {
public List<Student> getColleges();
}
interface AdditionalMethodsThatHaveBeenInB {
void methodThatWasInB();
}
interface C extends A<SeniorStudent> {
public SeniorStudent isCurrentStudent(String name);
public List<SeniorStudent> getStudentList();
public void addStudent(Attributes s);
}
interface D extends C, AdditionalMethodsThatHaveBeenInB {
public List<SeniorStudent> getColleges();
}
interface Attributes {}
interface Student {}
interface SeniorStudent extends Student {}
Of course, it's hard to tell whether this is also possible in your real code, but maybe a similar approach is applicable there.