Let's begin with a puzzle. Suppose we have a class Student which inherits from a class Person:
1
2
3
| public class Person { ... } public class Students extends Person { ... } |
We have the following variables:
1
2
3
4
| List<Person> personList; List<Student> studentList; Person[] personArray; Student[] studentArray; |
Which of the following assignments is correct?
1
2
3
4
| studentList = personList; personList = studentList; studentArray = personArray; personArray = studentArray; |
Solutions
1
| studentList = personList; |
Answer: incorrect.
This one is obvious to anyone. Surely a list of Persons cannot be assigned to a variable containing a list of Students because the list can contain Person objects which are not Students.
This one is obvious to anyone. Surely a list of Persons cannot be assigned to a variable containing a list of Students because the list can contain Person objects which are not Students.
1
| personList = studentList; |
Answer: incorrect.
This assignment is not correct because List<Student> is not a subclass of List<Person>. This is surprising to many Java beginners. Here's an explanation.
This assignment is not correct because List<Student> is not a subclass of List<Person>. This is surprising to many Java beginners. Here's an explanation.
Imagine for a moment that the assignment was correct. Then the variable personList would hold a reference to the studentList object (a list of Students). But the variable personList only knows that it is a list of Persons. One can try to insert a Professor into the list.
1
| personList.add( new Professor( "A" , "B" )); //incorrect but compiles |
The compiler does not know that the insert operation is incorrect. The runtime system has no way of knowing that the insert is incorrect. Recall from my previous post that at runtime the type of studentList object is List. All objects, including Professor, can be put into something of type List.
If List<Student> were a subclass of List<Person>, then the generics would not ensure type safety as it is supposed to guarantee.
1
| studentArray = personArray; |
Answer: incorrect.
This assignment is not correct, for the same reasons as the similar assignment with lists is not correct.
This assignment is not correct, for the same reasons as the similar assignment with lists is not correct.
1
| personArray = studentArray; |
Answer: correct.
Here we have another surprise. Arrays in Java are covariant, which, in our example, means that if Student is a subclass of Person (which it is), then Student[] is a subclass of Person[].
Here we have another surprise. Arrays in Java are covariant, which, in our example, means that if Student is a subclass of Person (which it is), then Student[] is a subclass of Person[].
Java arrays know the type of its elements and they check at runtime whether an element can be inserted into it.
Java arrays know the type of its elements and they check at runtime whether an element can be inserted into it.
1
| personArray[ 0 ] = new Professor( "A" , "B" ); //runtime exception |
If we try to insert a Professor into an array of Students (even if it's referenced via a Person[] variable) an ArrayStoreException will be thrown.
Again, it's not possible that an exception is thrown in a similar situation with parametrized lists. The generic lists do not know the type of its elements at runtime. They cannot checkwhether an element can be inserted into the list. As a consequence, List<Person> is not a subclass of List<Student>.
It is possible to have a variable which can hold both a list of Persons and a list of Students but you have to use a new construction, wildcards. But that's a topic for a different post.
it's not possible that an exception is thrown in a similar situation with parametrized lists. The generic lists do not know the type of its elements at runtime. They cannot checkwhether an element can be inserted into the list. As a consequence, List<Person> is not a subclass of List<Student>.
Read full article from Java Generics: The Difference between Java Arrays and Generic Lists | OneWebSQL