Issue
I'm trying to design my own programming language, and am thinking about generics. I've been doing Java for quite a while now and know about the extends
and super
generic bounds.
I'm reading href="http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ204" rel="nofollow">this post and trying to understand the need for the lower bounds.
In my language, I am planning to do generics the same way as a regular field, if you say List<MyObject>
, you can store either a MyObject
, or any subtype of MyObject
. Makes sense right?
On the post, they have the following class hierarchy:
class Person implements Comparable<Person> {
...
}
class Student extends Person {
...
}
They then have a sort method:
public static <T extends Comparable<T>> void sort(List<T> list) {
...
}
What I think, is that you should be able to send a List<Student>
to this method. As a Student
extends Person
, the compare
method would be handled by it's superclass, Person
.
The reason for the error message is that the compiler infers the type parameter of the sort method as T:=Student and that class Student is not
Comparable<Student>
. It isComparable<Person>
, but that does not meet the requirements imposed by the bound of the type parameter of method sort. It is required thatT
(i.e.Student
) isComparable<T>
(i.e.Comparable<Student>
), which in fact it is not.
The above doesn't make any sense to me...you should be able to do student.compare(person)
, so why doesn't this work?
Maybe it's saying that Student
should implement it's own comparable method so that Student
has a say in the comparison? You don't need to do anything special, just override Person
's method. You won't be able to guarantee you are comparing to another Student
, but that can be checked with instanceof
.
Is there something I'm missing here?
And after all this thinking, I'm now wondering what the purpose of extends
is. From my understanding, in a List<MyType>
, you can only put a MyType
in, not any of it's subclasses. As mentioned above, this doesn't make any sense to me and you should be able to put any subclass in the list like a field.
I should probably make this clear, it's not "why doesn't it work in Java", but "why doesn't it work in generics theory". I just tagged java because that is where I'm making my comparisons.
Solution
First: The method declaration
public static <T extends Comparable<T>> void sort(List<T> list)
does not make much sense for me. I thing it should be
public static <T extends Comparable<? super T>> void sort(List<T> list)
Then it would be possible to write sort(listOfStudents)
. Now I will explain the advantage of upper and lower bounded wildcards:
Polymorphism of type parameters is not transferred to it's generic type
This mean a list of students (List<Student>
) is not a list of persons (List<Person>
). A instruction like
List<Person> list = new List<Student>();
would fail in Java. There is a simple reason: list.add(new Person());
would be illegal for a list of students but not for a list of persons.
Upper Bounded Wildcards
But maybe you have a function which doesn't care whether the objects are subclasses or not. For example: You could have a method like this:
void printAll(List<Person> list)
They just print some data about all persons to stdout. If you have a list of students (List<Student> listOfStudents
) you could write:
List<Person> listOfPersons = new ArrayList<>();
for (final Student student : listOfStudents) {
listOfPersons.add(student);
}
printAll(listOfPersons);
But you may see that it isn't a very nice solution. Another solution would be to use upper bounded wildcards for printAll
:
void printAll(List<? extends Person> list)
You can write something like Person person = list.get(0)
in printAll
. But you cannot write print.add(new Person())
because list
could be a list of students or something else.
Lower Bounded Wildcards
Now the same in the other direction: Lets say you have a function which generates some students and put them in a list. Something like this:
void generateStudents(List<Student> list) {
for (int i = 0; i < 10; ++i) {
list.add(new Student());
}
}
Now you have a list of persons (List<Person> listOfPersons
) and want to generate students in this list. You could write
List<Student> listOfStudents = new ArrayList<>();
generateStudents(listOfStudents);
for (Student student : listOfStudents) {
listOfPersons.add(student);
}
You may see again, that it is not a very nice solution. You could also change the declaration of generateStudents
to
void generateStudents(List<? super Student> list)
Now, you can just write generateStudents(listOfPersons);
.
Answered By - JojOatXGME
Answer Checked By - Clifford M. (JavaFixing Volunteer)