SCJP – Generics

  • Generics it's a only compile time java feature. After the bytecode generation everything works as before.
  • It's not correct to use ? on the constructor invocation, the second part. We need to make an instance of a concrete type.
    • new ArrayList<?>(); // doesn't work because it must be instantiated with a concrete type
    • new ArrayList<Set<?>>(); // It works because the type is concrete, only the inner type is generic...

 

  • Polymorphic assignments applies only to the base type, not the generic type parameter.
    • List<Animal> aList = new ArrayList<Animal>(); // yes
    • List<Animal> aList = new ArrayList<Dog>(); // no
    • The polymorphic assignment rule applies everywhere an assignment can be made. The following are NOT allowed:
      • void foo(List<Animal> aList) { } // cannot take a List<Dog>
      • List<Animal> bar() { } // cannot return a List<Dog>
  • List<Number> list1 = null;
    List<? super Integer> list2 = null;
    list2 = list1;     // compiles Ok
  • Queue<?> q1 = null;
    Queue<Integer> q2 = null;
    q1 = q2;          // compiles Ok
  •  NavigableSet<?> set = new TreeSet<Object>();
    set.add(new Object());    // compiles error
  • class Test extends ArrayList<? extends Number> {}   // compiles error
  • Wildcard syntax allows a generic method, accept subtypes (or supertypes) of the declared type of the method argument:
    • void addD(List<Dog> d) {} // can take only <Dog>
    • void addD(List<? extends Dog>) {} // take a <Dog> or <Beagle>

 

  • When using a wildcard, List<? extends Dog>, the collection can be accessed but not modified.
    • When using a wildcard, List<?>, any generic type can be assigned to the reference, but for access only, no modifications.
    • The same for a map. read only access. map.put(...)
  • List<Object> refers only to a List<Object>, while List<?> or
    List<? extends Object> can hold any type of object, but for access only.
  •  If a method returns List<? extends Chewable>
    • the return value cannot be assigned to a List<Chewable>, The compiler don't know the concrete type of the returned list. Note this is only compile time check, so doesn't matter the list that is in runtime

Issue with calling add in List <? extends E> object

The compiler never know for which type the list will be instantiated.

  • void add(Set<? extends String> set) {set.add(new String("aas")); }  // Compiler error
  • void add(Set<? extends String> set) {set.add(null); }  // OK

 

// The a variable can be assigned with a ArrayList of E
         List <? extends E> a = new ArrayList<E>;
// or with a ArrayList of A (A extends E). The compiler never knows.
         List <? extends E> a = new ArrayList<A>;

public void addNewE(E e){
   a.add(e);
 }

This doesn't work because the object a could be instantiated with, for instance, new ArrayList<A> , when A extends E. So, when we are trying to invoke the the add method on List object, it is typified with T, that is the same T in add method parameter, and so we never know if the list if a list of E or a list of A, and for that the compiler gives an compiler error.

  • You can pass a generic collection into a method that takes a non-generic collection, but the results may be disastrous. The compiler can't stop the method from inserting the wrong type into the previously type safe collection.
    • If the method tries to add something to the collection, the compiler you raise a warning: potentially "unsafe".
  • The wildcard keyword extends is used to mean either "extends" or "implements.

 

 Issue with calling add in List <? super E> object

  • List<? super Number> queue6 = new ArrayList<Object>();       queue6.add(new Integer(1));
    • with any type that IS a Number of course that works.
  • List<? super Number> queue7 = new ArrayList<Object>();      queue7.add(new Object());
    • with any type super than Number doesn't work because queue7 could be instantiated with, for instance, ArrayList<Number>

Method override

class Parent {
   void say(List<String> list) {
       System.out.println("parent");
   }
}
class Child extends Parent {
   void say(List<Integer> list) {
      System.out.println("child");
}
This is a method override, but doesn't compile because must be a List of the same type.
  • my instanceof T (T is a generic type definition)
    • Cannot perform instanceof check against type parameter T.
      • Use instead its erasure Object instead since further generic type information will be erased at runtime
  • private T[] array = new T[7];   //T is a generic type. Cannot create a generic array of T. It's not possible to use generic type on constructor side.
  • It's possible to subclass a generic class, putting the type on the extends statement, and so the subclass is just a plain non- generic class. Here we can put all code that depends on the type.
    • Generics is not for the need of different implementation for the classes, it's for when we can have the same code dealing with different classes.

Warnings

  • List<String> lst1=new Vector();     //compiles OK, warnings
  • List lst2=new Vector<String>();     //compiles OK, no warnings
  • List<?> queue4 = new ArrayList();  //ok -  No compile error. With warnings.
  • NavigableSet<? extends Object> set1 = new TreeSet<Object>();        set1.add(new Object());
    • Doesn't work because compiler doesn't know the type of set1. For instance, if the type is TreeSet<String>, the compiler don't let us to add anything don't know it and you could try to add Integers: the variable definition lets

Tags:

Leave a Reply