Showing posts with label Generics. Show all posts
Showing posts with label Generics. Show all posts

Thursday, January 7, 2016

Caveat - JVM cannot check a generic cast at runtime

The Java Generics is based on Erasure, which means the type check is only done at compile time, and at runtime the type information is erased.

The following code will throw an ClassCastExceptionbecause there is no way the JVM can check the cast at runtime.

 public static void main(String[] args) {
  List<String> strings = createList(1);
  String string = strings.get(0); //ClassCastException here
  System.out.println(string);
 }
 
 public static <E> List<E> createList(Object object) {
  List<E> list = new ArrayList<>();
  E element = (E)object;
  list.add(element);
  return list;
 }

At runtime, the list object's type is just List. When using type parameter E to cast the object, the element's type is just Object and it's legal to add an Object to List. But when trying to get an element from the strings, the cast (generated by compiler) throws an ClassCastException as an Integer cannot be casted into a String.

Monday, July 9, 2012

Java Generics assignment sheet


raw type <------ any
any <------ raw type
<?> <------ any
<? extends T> <------ <T> and <? extends T>
<? super T> <------ <T> and <? super T>
<------: assign (assign from right to left)
any: all different variations of the generic class, including raw type and <T> and <?> and <? extends T> and <? super T>.
Example:


public class Main {


/**
* @param args
*/
public static void main(String[] args) {

List listRaw = new ArrayList();
List<Student> listQualified = new ArrayList<Student>();
List<?> listUnbounded = new ArrayList<Student>();
List<? extends Student> listUpBounded = new ArrayList<Student>();
List<? super Student> listDownBounded = new ArrayList<Student>();

// List raw type can take all different variations of List, for compatibility reason.
List rawList1 = listRaw;
List rawList2 = listQualified;
List rawList3 = listUnbounded;
List rawList4 = listUpBounded;
List rawList5 = listDownBounded;

// All different variations of List can take List raw type, for compatibility reason
listRaw = rawList1;
listQualified = rawList2;
listUnbounded = rawList3;
listUpBounded = rawList4;
listDownBounded = rawList5;

// List unbounded wildcard can take All different variations of List
List<?> wildcardList1 = listRaw;
List<?> wildcardList2 = listQualified;
List<?> wildcardList3 = listUnbounded;
List<?> wildcardList4 = listUpBounded;
List<?> wildcardList5 = listDownBounded;

// List subtype wildcard can take <T> and <? extends T> (and as mentioned above raw type)
List<? extends Student> subTypeWildcartList1 = listQualified;
List<? extends Student> subTypeWildcartList2 = listUpBounded;

// List supertype wildcard can take <T> and <? super T> (and as mentioned above raw type)
List<? super Student> superTypeWildcartList1 = listQualified;
List<? super Student> superTypeWildcartList2 = listDownBounded;

// You cannot create generic class instance with any form of wildcard
// List listNotAllowed1 = new ArrayList<?>();
// List listNotAllowed2 = new ArrayList<? extends Student>();
// List listNotAllowed3 = new ArrayList<? super Student>();

// An "exact" type of List (no wildcards) in a generic method argument can take all different variations of List.
// The return type is the same as calling the list.get(0),
// which is Object for List, List<?> and List<? super T>, and T for List<T> and List<? extends T>.
Object objectRaw = getFirstElement(listRaw);
Student studentQualified = getFirstElement(listQualified);
Object objectUnbounded = getFirstElement(listUnbounded);
Student studentUpBounded = getFirstElement(listUpBounded);
Object objectDownBounded = getFirstElement(listDownBounded);
}

public static <T> T getFirstElement(List<T> list) {
T t = list.get(0);
return t;
}
}

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.