Generics_Part 1

Generics was introduced from JDK 1.5 and since then been serving as a major feature in using Java language.

Generics is a function that does compile-time type checks for method or collection classes that deals with various types of objects; hence classes like HashSet, ArrayList, and HashMap make very good uses of generics.

1. Basics

Generics does…

enhances type stability : it prevents objects from being saved in unintended types, and reduces errors when an object is taken out in a different type than its original type.
simplifies the code : it lets you omit writing codes for type checking and casting.

package Generics;

public class Person<T> {
    T language;
    void setLanguage(T language){
        this.language = language;
    }
    T getLanguage(){
        return this.language;
    }
}
  • One thing to note is that ‘T’ can be substituted to basically any alphabet. Normally in case of arrayList, E (elements) is used; and for Map, K(key) and V(value) are often used.
        Person<String> person = new Person<>();
        person.setLanguage("Java");
        person.setLanguage(new Object()); //<---error!
  • The once-mysterious type T is not fixed to be a String. Once defined such, it has same as writing the whole class with String substituting T. Hence the error from the third line above. img
        Person<String> person = new Person<String>();
        person.setLanguage("Java");
        person.setLanguage(new Object()); //<---error!
        
        Person<Integer> person2 = new Person<Integer>();
        person2.setLanguage("Python"); //<---error!
        person2.setLanguage(0);
  • It is possible to make several Person objects, much like it is possible to call a same function multiple times with varying arguments.
  • It will follow quite automatically that static members can’t have a generics type, because they should be used in a same way regardless of object types.
What is okay and not
        public class Person<M> {
            M language;
            M[] languageArr; //OK
            
            M toArray(){
                M[] tmpArr = new M[languageArr.length]; //Error!
            }
        }
  • Declaring an array of generics type is okay, while instantiating a new one is not.
  • This is because new operator has to know exactly what type the T is during the compile time. Likewise, instanceof cannot be used because compiler has no way of knowing what T will be.

2. Using Generics

    public static void main(String[] args) {
        Person<JavaDeveloper> javaPerson = new Person<>(); // specific type of T can be ommited from JDK 1.7
        Person<JavaDeveloper> javaScriptPerson = new Person<JavaScriptDeveloper>(); //error

        Person<JavaDeveloper> groupOfJavaPersons = new Person<>();
        groupOfJavaPersons.add(new Java());
        groupOfJavaPersons.add(new JavaScript()); //error!
    }
    
    public class Programmer{}
    public class JavaDeveloper extends Programmer{}
    public class JavaScriptDeveloper extends Programmer{}
  • This is quite self-explanatory. Once declared with specific types, the created objects won’t tolerate other types to butt in.

3. Limited Generics Class

  • As mentioned in beginning, generics was created to lessen the burden for type-checking and casting. So far there doesn’t seem to be much virtue in it. how about limiting the type T to only take certain types of objects?
public class Person<T extends Programmer>
  • This forces T to be descendents of Programmer. Hence, the below is possible.
      Person<Programmer> programmerBatch = new Person<>(); 
      programmerBatch.add(new JavaDeveloper());
      programmerBatch.add(new JavaScriptDeveloper());
    
  • Basically if you omit and just write <T>, it is same as <T extends Object>

  • When it comes to implementing an interface, make sure to use extends, rather than the usual implements.
    interface codable()
    class programmerBatch<T extends codable>

4. Wildcard Expressions

public class Outsourcing {
    static webPage makeWebPage(Person<Programmer> box){
        String temp = "Coded by ";
        for(Programmer p : box.getList()) temp += p + " ";
        return new webPage(temp);
    }
}

Above class has the following problems.

  • (1) Since it is a static method, a specific type (programmer) has to be written
  • (2) Because of (1), if you want to substitute programmer to JavaDeveloper or JavaScriptDeveloper, you have to make separte methods like
      static webPage makeWebPage(Person<JavaDeveloper> box){
        String temp = "Coded by ";
        for(Programmer p : box.getList()) temp += p + " ";
        return new webPage(temp);
    }

      static webPage makeWebPage(Person<JavaScriptDeveloper> box){
        String temp = "Coded by ";
        for(Programmer p : box.getList()) temp += p + " ";
        return new webPage(temp);
    }
  • (3) However, this is NOT possible, because having different generic types does not allow overloading.
  • (4) This can be handled with ?(wildcard)!

<? extends T> : upper limit. T and its descendents <? super T> : lower limit. T and its ancestors <?> : no limit. same as <? extends Object>

  • Converting the problematic makeWebPage function,
 static webPage makeWebPage(Person<? extends Programmer> box){
        ...
    }
  • Now this makeWebPage can accomodate JavaDevelopers and JavaScriptDevelopers at once! (and any of Programmer’s descendets)

  • Going back to the link, I can conclude that Generic type T is a compile-time decided consistent type for class, whereas wildcard is an expression limiting the range of types. Those two are not of same nature, the former being a grammar and the latter a little like a hack.

5. Generic Method

    static <T> void sort(List<T> list, Comparator<? super T> c)
  • The T in method and T in class are ‘totally different’ things. They may or may not be the same, and utterly unrelated.
 static webPage makeWebPage(Person<? extends Programmer> box){
        ...
    } //can be changed into ...

 static Person<? extends Programmer> webPage makeWebPage(Person<T> box){
        ...
    }
  • Below is some tricky example
      public static <T extends Comparable<? super T>> void sort(List <T> list)
    
  • Okay. parameter ‘list’ is of type T that is a decendents of Comparable<? super T> for one.
  • The comparable interface compares T or its ancestors.
  • If the list is given as JavaDevelopers, the comparable compares JavaDevelopers, Programmers and Persons and Objects