Sunday, July 8, 2012

Java Generics Wildcards explained


Given a generic class, like List<T>, or any generic class:
When using this generic class, for example:
    • List<Student> students = StudentFactory.getStudents();
    • or
    • public class StudentList implements List<Student> {...}
    • or
    • List<? extends Student> students = StudentFactory.getStudents();
    • or
    • List<? super Student> students = StudentFactory.getStudents();
    • or
    • etc.
public class StudentFactory {
public static List<Student> getStudents() {
return Arrays.asList(new Student("Student"));
}
}
1.In the case of using <? extends Student>
    • If a method takes a parameter of type T in the generic class definition (in this case, add(T t) of List<T>), we cannot call this method. (except by passing a null, which is not interesting)
    • If a method returns type T in the generic class definition (in this case, T get(int index) of List<T>), calling this method returns Student.
2.In the case of using <? super Student>
    • If a method takes a parameter of type T in the generic class definition (in this case,  add(T t) of List<T>), we can call this method by passing any subtype of Student.
    • If a method returns type T in the generic class definition (T get(int index) of List<T>), calling this method returns Object.
Explanations:
For case #1, List<? extends Student> means a list of any sub-type of Student.
students can point to a List<MathStudent>, or to a List<HistoryStudent>. That is to say, a List<MathStudent> can be ‘upcasted’ to a List<? extends Student>. If a method using type T accepted others to call it with type T, in this case T is Student. Inside the method, we could do something like assigning a HistoryStudent to MathStudent, which will surely cause an ClassCastException. So the compiler refuses calling these kind of methods using T at compile-time to do avoid this run-time exception. That’s kind of one of the important purpose of Generic. But it’s safe to call a method which returns a T since, we are sure that it returns MathStudent or HistoryStudent or Student. Whether it’s a MathStudent or a HistoryStudent or a Student, it’s always a Student.
For case #2, List<? super Student> means a list of any type that Student derived From, such as People, and People’s father class, etc. 
students can point to a List<People>, (suppose that Student extends People) or a list of any base-type of Student, but students cannot point to List<MathStudent>, or List<HistoryStudent>, or a list of any sub-type of Student. That is to say, a List<People> can be ‘upcasted’ to a List<? super Student>, but List<MathStudent> cannot be ‘upcasted’ to a List<? super Student>. Methods using type T  are allowed to be called with type T or any subtype of T, in this case, T is Student. because it’s safe. Inside the method, you can assign Student or MathStudent or HistoryStudent  to Student/People (or to any father class of Student). But when a method returns type T, it cannot be sure which base class of Student it returns, so it returns Object.

0 comments:

Post a Comment