Using Java Generics


Published on

This is a presentation on using Java Generics by Intertech.

Published in: Technology, News & Politics

Using Java Generics

  1. 1. Fundamental Generics Programming 1. What are generics? 0. Similar to templates in C++, but done in a different way 1. They are used to allow the compiler to help remove casting errors 2. They are new to JDK1.5 3. Unfamiliar to most programmers (as of 2004, they will be common soon as .Net and Java both implement them) 2. To explain it might be best to just look at a simple example class GenericBean<VarType>{ private VarType x; public GenericBean(){} public GenericBean(VarType x){ this.x = x; } public VarType getX(){ return x; } public void setX(VarType x){ this.x = x; } }
  2. 2. 3. The example above is simplified to show just the facts. 4. The new syntax looks a bit like an XML tag: <VarType>, you can use any valid Java identifier, the author chose VarType for illustration 5. This “Generic” reference sets up a class that can hold different types, and still get compiler support. 6. When using this “GenericBean” you can declare the type that <VarType> will represent, like the code below. GenericBean<String> bean = new GenericBean<String>(); 7. When you create the object with the generic type defined, the compiler will allow you to skip all casts and still protect you bean.setX("Hello"); //the setX allows only strings String greeting = bean.getX(); //the getX return a string 4. Comparing this to the pre-1.5 versions of Java, you can see the significant difference. class GenericBean{ //Not using Generics private Object x; public GenericBean(){} public GenericBean(Object x){ this.x = x; } public Object getX(){ return x; } public void setX(Object x){ this.x = x; } }
  3. 3. 5. The above GenericBean contains a field that can hold any object type, but the compiler doesn’t help you. 6. To demonstrate, code to use the GenericBean would look like the below. GenericBean bean = new GenericBean(); bean.setX("Hello"); //You can pass in any type String greeting = (String)bean.getX(); // You must cast 7. Usually when casting you should protect vs. a class cast exception which would introduce a touch more code. GenericBean bean = new GenericBean(); bean.setX("Hello"); String greeting = null; if (bean.getX() instanceof String){ greeting = (String)bean.getX(); } 8. Comparing the code side by side demonstrates that the client using the generic benefits from easier to use types. GenericBean bean = new GenericBean(); bean.setX("Hello"); String greeting = null; if (bean.getX() instanceof String){ greeting = (String)bean.getX(); } GenericBean<String> bean = new GenericBean<String>(); bean.setX("Hello"); String greeting = bean.getX();
  4. 4. 9. The above simple example does not fully demonstrate how annoying casting can get in Java. We need to mention two things to see why generics are a desired modification. 1. Using the instanceof check in your code detects a problem at runtime, not compile time. 2. Placing specific types (like strings or whatever) into java.lang.Object references is really common, especially in the Collections API (ArrayList, HashSet, Iterator …). 10.First we look at compile time vs. runtime checking. 8. Extending the example we already have the Generic using GenericBean type will prevent anyone from misusing it by inserting a non-String type. GenericBean<String> bean = new GenericBean<String>(); StringBuffer buff = new StringBuffer("Goodbye"); bean.setX(buff); //this won’t compile 9. The following compiler error will be generated. setX(java.lang.String) in GenericBean<java.lang.String> cannot be applied to (java.lang.StringBuffer) bean.setX(buff); ^ 10.So an attempted insert of a StringBuffer into a <String> typed generic method causes a compiler error. 11.In general compiler errors are preferred because they are typically easier to fix than runtime errors. (usually 10 times easier)
  5. 5. 11.To further demonstrate the compile time vs. run time advantage, and to see how common casting is we introduce an common Collections example. 12.If you are an experienced Java programmer you might already know how common the check/cast code is in Collections. 13.In the below example we create a very simple method that takes any collection and prints out the second character of each String held in the collection. public static void firstChar(Collection c){ //prints out the second character of each element Iterator itor = c.iterator(); while(itor.hasNext()){ Object o =; if (o instanceof String){ System.out.println(((String)o).charAt(1)); }else{ //?This shouldn't happen do I ignore or, //do I throw an exception here? } } } 12.Notice a few key things about the code: 14.It was written generically as we could pre-1.5 to handle any type of collection. 15.Each element we get from the Iterator must be checked using instanceof. 16.We must cast the object to a String to use the “charAt” method. 17.If the collection contains something that isn’t a string, you as a programmer must decide what to do. You can’t
  6. 6. always (successfully) assume that it will only contain strings.
  7. 7. 13.Many programmers new to Java, realize immediately that is this an ugly piece of code. 14.Rewriting this using the power of generics we get better compiler support. public static void firstChar(Collection<String> c){ //prints out the second character of each element Iterator<String> itor = c.iterator(); while(itor.hasNext()){ System.out.println(; } } 15.To invoke this method use code like the following. ArrayList<String> stringList = new ArrayList<String>(); stringList.add(“Hello”); firstChar(stringList); 16.Using the new for-each ability of the for-loop makes the code a touch simpler. public static void firstChar(Collection<String> c){ //prints out the second character of each element for(String s: c){ System.out.println(s.charAt(1)); } } 18.The details of the new for loop will not be covered in this material. 17.The firstChar method can only be invoked using a collection that contains only strings. 19.The compiler will prevent you from invoking the method using an ArrayList designed to hold StringBuffers, or any other type.
  8. 8. 18.Before moving on to problems, details and comparisons to other languages you should feel comfortable with what and why. 20.Generics allow you to create classes with “flexible” typing, the types are defined when the object is created not when the class is created. 21.Generics allow the compiler to change method signatures when the class is being used. 22.The main benefit of Generics is the compile time checking of types. 23.The most common (best known) use of generics in Java is the creation of typed Collections, which removes much of the annoying casting previously needed when dealing with collections. 19.New versions of Java are very backward compatible with old versions of Java. 24.This is done to minimize impact of moving to a new version of Java. 25.Focusing on backwards compatibility can be controversial because it can cause uglier solutions. 26.One of the reasons Java thrived was because the earliest creators of Java decided to make it not backwards compatible with C++. 27.The changes introduced in 1.5 introduce very few backward compatibility issues. (≈ 1 error per 1,000,000 lines of code)
  9. 9. 20.In later sections we will look at some of the decisions made in the Java Implementation of Generics and their advantages and Disadvantages.
  10. 10. Raw Types 21.The changes needed to create generics in Java are two fold: 28.The compiler was changed to recognize the <xyz> syntax and create valid bytecode. 29.The java.util package was rewritten to take advantage of generics. All collection types and many related types have been rewritten to use generic typing. 22.We will see some of the compilers tricks later, for now look at a snippet of the ArrayList type in its new form. package java.util; public class ArrayList<E>{ public void add(E o){ //... } pulbic E get(int index){ //... } //... } 23.The new ArrayList type is a generic class so when you create one you will create one to hold a specific type. ArrayList<Shape> shapeList = new ArrayList<Shape>(); 24.This is a good thing (get used to the new syntax quick), but what happens when I compile my old code? 30.The old code doesn’t have the fancy <type> notation in it. ArrayList list = new ArrayList();
  11. 11. 25.The old style of code is still supported through the use of Raw Types. 31.The ArrayList created without a type will work like the original Array List. This is called a “Raw Type”. 32.The list created is not the same as a list created to hold java.lang.Objects! ArrayList<Object> objectList = new ArrayList<Object>(); ArrayList rawList = new ArrayList(); //These two references are not the same! 33.The compiler treats these two types differently; the raw type is allowed to get away without type safety (for backwards compatibility). ArrayList<String> stringList = new ArrayList<String>(); rawList = stringList; //This is OK with Compiler objectList = stringList; //This is not OK with compiler
  12. 12. 26.Why were Raw Types introduced? 34.Raw types allow support for all methods that used the original definitions of any new generic types. (Legacy Code) 35.There are millions of Java developers coding in pre- generic Java code and billions of lines of code. 36.A simple example method that might have been created in JDK 1.4 follows below. /** * Iterates through the collection and gives each employee in * the staff collection a bonus based on business rules */ public void bonusTime(Collection staff){ Iterator itor = staff.iterator(); while (itor.hasNext()){ ((Employee); } } 27.Why not use java.lang.Object instead of raw types? 37.The simple answer is for type safety. Consider the following code. ArrayList<Dog> dogList = new ArrayList<Dog>(); ArrayList<Object> objectList = dogList; objectList.add(new Cat(“Morris”)); 38.This code will not compile, the second line will fail. 39.The compiler will not allow this to prevent the third line of code, where a Cat is added to the list that previously held only Dogs.
  13. 13. 28.The downside of this Raw Type backwards compatibility is type safety problems. 40.Consider the following code which unlike the previous example will compile. ArrayList<Dog> dogList = new ArrayList<Dog>(); ArrayList rawList = dogList; rawList.add(new Cat(“Morris”)); 41.The result of this code is an ArrayList that has a Cat in it even though it should only hold Dogs, 42.The code will compile and run, but if the Cat is ever referenced using the dogList reference it will throw a java.lang.ClassCastException. 43.The compiler will warn you when this might happen, the warning is introduced to guide you, but not force you to make the right decision 29.Raw Type take away: 44.Mixing old style Raw Types and new Generic types can cause some problems. 45.If possible minimize the mixing of pre-generic collection code with post generic collections code. 46.If you are generalizing existing types be aware that you get Raw Type backwards compatibility.
  14. 14. Bridging 30.As mentioned earlier the Java bytecode didn’t have to change to support generics. 47.The compiler was modified. 48.Existing classes (especially collections) were modified to use generic syntax. 31.This means little to the average Java developer, but means quite a bit when you look at some of the details. 49.The compiler is removing the need to write code to cast and check the cast using instanceof by inserting that code for you. 50.In some cases the compiler will add methods to maintain backwards compatibility. This insertion of code is called bridging.
  15. 15. 32.One of the simplest examples is in the use of the Comparable Interface. 51.The comparable interface has been improved using generics; a snipped of the interface appears below. package java.util; public interface Comparable<E>{ public int compareTo(E o); } 52.The improvement is in the generic type. It allows you to write a much cleaner compareTo method. public class MyBean implements Comparable<MyBean>{ int data; public int compareTo(MyBean o){ return data -; } } 53.Unlike the earlier version of the compareTo method this one doesn’t require a cast. 54.The compiler will check type safety when comparing so you don’t need to write checking and casting code. The compareTo implementation much simpler and more direct (a good thing).
  16. 16. 33.The Bridging occurs because some code might exist that expects you to implement the Comparable interface, not the Comparable<MyBean> interface. 55.If you decompile the Java code you will see that the compiler has added on method to your MyBean type. public class MyBean implements Comparable<MyBean>{ int data; public int compareTo(MyBean o){ return data -; } //created by compiler - Bridging public int compareTo(Object o){ return compareTo((MyBean)o); } } 34.This bridging is done to preserve backward compatibility, allowing previously written methods to work: public void pointless(Comparable x){ x.compareTo(new Object()); //This should compile and might throw ClassCastException }
  17. 17. Type Information 35.Generic types don’t have all runtime typing available to them. 56.This means that at runtime you can’t dynamically “discover” what type a generic is holding. 57.If this material was attempting to sell instead of inform this information wouldn’t even be mentioned. 58.To demonstrate we will look at another simple example, the following method prints out true if two objects are of the same type. public static void typesEqual(Object o1, Object o2) { Class type1 = o1.getClass(); Class type2 = o2.getClass(); System.out.println(type1.equals(type2)); } 59.A less rigorous way would be to use the instanceof operator like so: public static void instanceOfPrint(Object o1, Object o2) { System.out.println(o1 instanceof o2); } 60.When either of these methods is called on parameterized type the, even if the two types are holding different kinds of object the result is true. ArrayList<Dog> dogList = new ArrayList<Dog>(); ArrayList<Cat> catList = new ArrayList<Cat>(); typesEqual(dogList, catList); 61.When the code snippet above was run it printed out “true”.
  18. 18. 62.The type of ArrayList<Dog> is the same type as ArrayList<Cat>.
  19. 19. 36.Another result of the same decision with typing is that static variables can’t be generically typed. public class StaticDemoBean<VarDemo> { //This won’t compile static VarDemo x; } 63.When you attempt to compile you will see the following error. non-static class VarDemo cannot be referenced from a static context static VarDemo x; ^ 37.Java keeps all type information for the whole class, and each individual object defines the variable type.
  20. 20. Erasure 38.To create a backwards compatible and type safe generics implementation the compiler inserts casts and removes references to generic types. 64.This process is done in more than one pass of the compiler. 65.The process is commonly called “Erasure”. 39.When compiling a generic class the compiler will do erasure in the following general steps. 66.If a generic type is never used, no need to do anything. 67.The compiler will change a generic reference to be the most specific type is can. 68.The compiler inserts class casts as needed to get your code to work. 40.These steps are important for a couple of reasons. 1. You might see the compiler complain about two methods having the same “erasure” which you will need to work around. 2. The compiler will only allow you to invoke methods that are guaranteed to be present. 41.Point 2 above is one key difference between Java and many other implementations of generics. 69.We will look into the differences in detail a bit later.
  21. 21. 42.Looking at a basic example we can see why the Java bytecode didn’t need to be changed to work with generics. 70.This is the original source code of a “Generic” type called EBean. public class EBean<VarDemo> { VarDemo data; //Since the compiler know little it forces us to treat the // data reference as an Object. You can’t call methods not // defined in interface of java.lang.Object without a cast public void setData(VarDemo d){ = d; } public VarDemo getData(){ return data; } } 71.The compiler will compile this code forcing you to treat the “data” reference as an object. 72.Since this class is not yet used the compiler won’t insert any casts. 43.When you write the client application or any class that uses EBean the compiler will insert type casts for you. 73.This is a snippet of code created to test the EBean type. EBean<String> eBean = new EBean<String>(); eBean.setData("Hello"); String s = eBean.getData(); 74.The compiler will insert extra casts as it needs to. 75.The following is the decompiled version of the code, notice the lack of <String> and the addition of (String).
  22. 22. EBean ebean = new EBean(); ebean.setData("Hello"); String s = (String)ebean.getData();
  23. 23. 44.This erasure can create minor collisions in types if the type erasure causes a collision in a method signature. 76.The following example shows an erasure collision. public class EBean<VarDemo> { //... public void erasureIssue(String s){} public void erasureIssue(VarDemo vd){} } 77.This code will compile, unless any client code attempts to make a EBean that holds Strings and calls the erasureIssue method. EBean<String> eBean = new EBean<String>(); eBean.erasureIssue(""); 78.The compiler can’t figure out which “erasureIssue” method to call so it will cause a compiler error (below). reference to erasureIssue is ambiguous
  24. 24. Using extends in a Variable Declaration 45.Because the compiler uses erasure and assumes a minimum type when compiling, the Java compiler introduces new syntax to set a ‘minimum’ assumed type. 46.We will look at an example using the previously introduced Animal, Cat, Dog, and Rhino hierarchy. 47.The following is a definition of a bean that can hold any type of animal, but the animal type is generic. public class ExtDemoBean<Var extends Animal> { Var pet; public void setPet(Var d){ = d; } public Var getPet(){ return pet; } public void feedingOccured(FoodEvent evt){; } } 79.Because the pet reference is of type <Var extends Animal> the compiler can assume it is an animal and will allow you to call any method defined in Animal, not just Object. 48.This means our original syntax could have been written like the following with no effect. public class EBean<VarDemo extends Object> { //... }
  25. 25. Wildcards 49.One of the most unusual (or ugly) syntaxes that generic brings is the use of wildcards. 80.A wildcard represents a flexible type, one you don’t care about the exact type in the method. 81.The wildcard is useful because of how generic references must work with inheritance while keeping type safety. 50.Before showing the wildcard syntax, we will look at why the wildcard syntax exists. 51.In the below code we will create a method that can feed any collection of animals (or will it?). 82.The class hierarchy used is similar to one used in teaching inheritance to Java 101 students. 83.You should assume a very simple class hierarchy where the Dog, Cat, and Rhino types all extend from some base class called Animal and the Animal type defines an “eat()” method. public static void feedTheAnimals(Collection<Animal> animals){ //recall the for loop supports for-each syntax for (Animal iAnimal: animals){; } } 84.This method can now be used to feed any collection of animals. 85.In the below code we create a collection of animals and call feedTheAnimals. ArrayList<Animal> zoo = new ArrayList<Animal>(); zoo.add(new Rhino());
  26. 26. zoo.add(new Dog()); feedTheAnimals(zoo); 52.Although this looks like it should work fine, it has trouble if you try to feed different types of collections. 86.Collections that hold only Dog or Rhino types won’t be able to call this method. ArrayList<Dog> puppyStore = new ArrayList<Dog>(); puppyStore.add(new Dog()); puppyStore.add(new Dog()); feedTheAnimals(puppyStore); //Fails to compile 87.An ArrayList<Dog> object can’t be assigned to a Collection<Animal> reference. Collection<Object> x = new ArrayList<Dog>(); //ILLEGAL! 53.This limitation makes sense if you think about it in context. 88.If you could assign an ArrayList<Dog> object to a Collection<Animal> reference you could add cats to the dog list (a bad thing> ArrayList<Dog> puppyStore = new ArrayList<Dog>(); puppyStore.add(new Dog()); puppyStore.add(new Dog()); Collection<Animal> pets = puppyStore; //Illegal pets.add(new Cat()); //This would put a cat in the puppyStore 54.For about 50% of experienced object oriented programmers, looking at the code this seems simple. 55.To others (like me) it seemed a bit non-intuitive.
  27. 27. 56.How to you write more flexible method argument lists? Use wildcards. 89.Wildcard syntax allows you to define an argument/variable that fits a certain inheritance structure, not just one type. public static void feedTheAnimals2(Collection<? extends Animal> animals) { for (Animal iAnimal: animals){; } } 90.The above method feedTheAnimals2 uses a wildcard to create a more flexible method. 91.Since you never refer to the generic type actually used not name is given, just a question mark(?). 92.Unlike in the original feedTheAnimals, this method allows you to successfully invoke using a collect that holds sub-type of Animal. ArrayList<Animal> zoo = new ArrayList<Animal>(); feedTheAnimals2(zoo); // or this would work ArrayList<Dog> puppyStore = new ArrayList<Dog>(); feedTheAnimals2(puppyStore); 57.Wildcards create a more flexible argument list, needed in the Java implementation of generics because of the type safety constraints.
  28. 28. 58.There is one interesting side effect of using a wildcard. Wild carded Generic references won’t allow you to call setters. 93.The loss of setters makes wild carded references limitedly immutable. 94.To Demonstrate let’s look at what won’t work by extending the previous example. public static void feedTheAnimals3(Collection<? extends Animal> animals) { animals.add(new Cat()); //won’t compile for (Animal iAnimal: animals){; } } 95.The compiler doesn’t know if you can add a Cat to the “animals” reference or not, so it doesn’t allow it. 96.Any method defined generically will fail to compile because the compiler can’t figure out which type that method takes. 97.Looking at a snippet of Collection you might be able to see why. The add method’s actual signature is unknown at compile time. package java.util; public interface Collection<E>{ pubic void add(E e); } 98.What type is e inside the feedTheAnimals3 method? It is a “? extends Animal”, which may or may not be a Dog, Cat, or Animal.
  29. 29. 59.It is also possible to use wildcards with super instead of extends. 99.We will not cover this in detail, but below is a short example demonstrating what the code would look like. public static void superExam(ArrayList<? super Dog> dogParentList){ dogParentList.add(new Dog("Brano", "RedBrown")); Animal a = (Animal)dogParentList.get(0); } 100.When using super you can insert, but all return types will be of type Object.
  30. 30. Generic Methods 60.In all of the previous example we have created generic types, but methods can be generic too. 101.The syntax is very similar to generic types, to understand we will introduce a few examples. //a and b can be any like type public static <E> boolean foo(E a, E b){ return a.equals(b); } 102.The above example would work with any two objects because they are both object types. (so has little value except for syntax) 103.The below defines a method that will accept any two types of Animals. public static <E extends Animal> boolean foo2(E a, E b){ return a.equals(b); } 104.Again this is of little value; we could use just Animal references instead. 105.The below show how to establish a relationship in a method. public static <E, SubE extends E> boolean foo3(E a, SubE b){ return true; } 106.The references a and b are related through inheritance which is defined earlier in the signature.
  31. 31. 61.A more useful generic method could define the generic types being passed in. public static <Var> boolean areContentsEqual(Var[] array, Collection<Var> collection){ //... compare array to collection contents } 62.You could also use the extends syntax to insure both variable types are in the same lineage. public static <Var extends Animal> void transferAnimals(Collection<Var> from, Collection<Var> to){ for(Var v : from){ to.add(v); } } 107.Both of these collections will hold the same type of parameter so the add method is safe.
  32. 32. How do Java Generics Compare? 63.It is interesting and informative to compare Java’s generics to other implementations of generics. 64.We will discuss several differences. 1. The type safety enforces by the Java compiler. 2. The lack of support for primitive data types. 3. The lack of need to expose “template” source code. 65.One difference is how restriction is placed on types. 66.Java is very type safe in its implementation. 108.Is the type safety constraint a good thing or a bad thing? 109.The author of this material doesn’t have pat answer to this question, this makes for great debates. 67.To understand the difference you need to understand what other languages allow, that Java doesn’t. 110.Java uses a technique called “Erasure” that maps a generic reference to the lowest common denominator (we saw this). public class EBean<VarDemo> { VarDemo data; public void foo(){ data.toString(); //data is treated like a java.lang.Object by compiler } }
  33. 33. 68.In other languages the compiler may allow you to call any method on the data reference. 111.The following code snippet is a total fabrication using Java to demonstrate what another language might allow. public class EBean<VarDemo> { VarDemo data; public void foo(){ data.draw(); } } 112. This allows a more soft-typing (generic typing) of a reference. 113.The compiler would only check for the existence of a draw() method when the user creates a the EBean. 114.Again this code is fabricated for demonstration and won’t compile in any known environment. EBean<Shape> a = new EBean<Shape>(); EBean<GunFigther> b = new EBean<GunFighter>(); 115.The compiler will verify then each EBean is created if a draw() method exists, and in the above example they might. (semantics aside).
  34. 34. 69.In Java the Shape and the GunFighter would need to implement the same interface to get this code to work. 116.This is why we have the “extends” addition to Java generics. public class EBean<VarDemo extends Drawable> { ... } 117.This syntax ensures all creators of the EBean will use some implementation of Drawable. 118.This is a limitation in the language, not likely to go away in a later release. Is it good or bad? 70.To answer this question you need to define what good is, but that is just going too deep. 119.Does this limitation restrict what a software engineer can do to make a more flexible/maintainable/better code library? 120.Does this limitation serve as a guide to keep programmers from going to far astray?
  35. 35. 71.Another limitation of Java generics is the lack of primitives support. EBean<int> bean = new EBean<int>(); 121.The above code won’t compile because you can’t use a primitive in a generic. 122.This compiler error was produced when I attempt to compile. unexpected type found : int required: reference EBean<int> bean = new EBean<int>(); ^ 123.This limitation may be changed in future releases of Java. For a good solutions you might want to look into auto-boxing which is included in JDK 1.5 EBean<Integer> bean = new EBean<Integer>(); bean.setData(8); int i = bean.getData(); 72.The last issue I want to mention is how Java doesn’t require sharing source, or source code bloating. 124.You can ship a generic type as a bytecode. 125.The source code for the generic doesn’t need to be shared with users of the generic code. 126.Generics aren’t shared templates in Java like they are in some other languages.
  36. 36. Resources for Continuing Learning 73.In the compilation of this material several great sources were used. These are some great links to information if you need more. 74.For a short introduction to Generics and the basics of the syntax. 127.Close article in JDJ: 128.Sun’s Generics PDF Tutorial: 75.To help understand bridging and parts of erasure you can decompile your Java Code. 129.JAD: 130.JAD based IDE: 76.A couple of good basics, wildcards, erasure and bridging articles. Both written by William Grosso. 131. s.html 132. html 77.Fundamental links for Generics information 133.JSR-14: cs/
  37. 37. 134.JDK 1.5 download: 135.Gilad Bracha’s link: 78.For a more controversial/impartial look at generics. 136.Bruce Eckel’s input: