working with java generics

21
All rights reserved. Reproduction and/or distribution in whole or in part in electronic, paper or other forms without written permission is prohibited. Java ReferencePoint Suite SkillSoft Corporation. (c) 2002. Copying Prohibited. Reprinted for Balaji Nallathambi, Verizon [email protected] Reprinted with permission as a subscription benefit of Books24x7, http://www.books24x7.com/

Upload: debojyotis

Post on 26-Sep-2015

40 views

Category:

Documents


3 download

DESCRIPTION

generics

TRANSCRIPT

  • All rights reserved. Reproduction and/or distribution in whole or inpart in electronic, paper or other forms without written permission

    is prohibited.

    Java ReferencePoint SuiteSkillSoft Corporation. (c) 2002. Copying Prohibited.

    Reprinted for Balaji Nallathambi, [email protected] with permission as a subscription benefit of Books24x7,http://www.books24x7.com/

  • Table of Contents Point 9: Working with Java Generics..............................................................................................1

    Introducing Generics........................................................................................................................2 Overview of Generic Classes..................................................................................................2 Implementing Generic Classes...............................................................................................3

    Using Generics with Interfaces.......................................................................................................8 Implementing Generic Interfaces............................................................................................8 Subtyping and Generics........................................................................................................11

    Using Wildcards..............................................................................................................................13 Working with Wildcards.........................................................................................................13 Using Generic Methods........................................................................................................17

    Related Topics................................................................................................................................19

    i

  • Point 9: Working with Java GenericsS. KartikeyanThe generics programming model is an extension to the Java programming language introducedwith the Java Development Kit (JDK) 1.5. Generics allow you to specify the object type to beassociated with a container class when instantiating the class. As a result, you can avoidtypecasting code when working with generic collection classes. Using generics reduces the amountof code and makes programs more readable and less prone to errors.

    This ReferencePoint introduces generics and explains the use of generics to increase thereadability of Java programs. It also explains how to create and use Java programs that implementgenerics.

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • Introducing GenericsGenerics allow you to design Application Programming Interfaces (APIs) that provide generalfunctionality applicable to multiple types. The type of an object is the information about the classand hierarchy of classes and interfaces to which the object belongs. Using generics you can specifythe type information of data using a parameter, which means that the type can be a variable or itcan change at runtime.

    Generics is a popular model of programming supported by functional languages, such as Haskell.Various objectoriented languages, such as C++ and Eiffel, also support generics. The C++language implements generics through templates.

    Note A functional language is a language that supports and encourages programming in afunctional style. Functional programming is a style of programming that evaluatesexpressions, rather than executing commands with variables. For example, to calculate thesum of integers from 1 to 10, a functional programming language uses an expression, suchas sum [1..10], rather than executing commands such as:

    total = 0;

    for (i=1; i

  • The first line in the above snippet creates an ArrayList object that stores only strings. When youretrieve the String object from the ArrayList object, you do not have to typecast it. The followingsnippet shows how to retrieve a String object from the ArrayList object, without writing the code fortypecasting:String str = strList.iterator().next();

    You specify the parameter type when instantiating an object of the class. For example, if you createa class, Graph, which can hold multiple types of data within it, then the declaration for this classwould be:

    public class Graph extends SomeClass implements SomeInterface{}

    The above snippet specifies the parameter type of the class within angled brackets as A. You usethis convention when you create custom generic classes. Using a single capital letter is notmandatory, but doing helps avoid confusion with other data types and class names. JDK 1.5replaces the entire collection framework in the java.util package with a generic collection framework.As a result, you can use the generic versions of collection classes in Java programs.

    The addition of generic extension to the Java language does not call for a change in Java VirtualMachine (JVM). This is because the Java compiler applies generics only at compile time. It uses theparametric information in the source code to provide type safety.

    Note Type safety means an object retains its original type, and errors due to type mismatchare trapped during compile and runtime. A language and tools associated with thelanguage should ensure type safety if the language is type safe.

    The compiler compiles the source using a technique called erasure. Erasure ensures that thecompiler does not retain any generics code of the source code in the compiled code. The compilerinserts appropriate casting corresponding to the instances of generics code into the bytecode toreflect the parametric information. As a result, the compiled bytecode of classes written usinggenerics do not contain any information relating to generics. This means code written using JDK 1.5is backward compatible with JRE 1.4.

    JDK versions prior to 1.5 implement a subtyping rule by which all objects are of type Object. Thisallows you to store objects of multiple types in one data structure or collection class. When creatingarrays you always mention the type of the object to be stored in the array. Generics allow you treatother data structures and collection classes similar to arrays, whereby you can explicitly state that adata structure can hold objects of only one type.Implementing Generic ClassesGenerics are useful when you develop custom data structures. For example, you can create acircular linked list using the generic model, which ensures type safety to all other classes that mayuse the circular linked list.

    Note The collection framework does not implement circular linked lists.

    Circular linked lists are linked lists whose last node refers back to the first node of the list. This typeof list is useful when you want to keep traversing the list and accessing the elements in the listrepeatedly in a circular fashion. For example, you can use a circular linked list when you want tocreate an animation using few still images as frames.

    Java ReferencePoint Suite 3

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • Note To learn more about linked lists and other data structures, see the ImplementingLinked Lists in C++ ReferencePoint.

    To create a circular linked list using generics, create two classes, Node and CircularList. Listing191 shows the complete code for the CircularList.java file.Listing 191: The CircularList.java File

    class Node{ //Declaring the element of type A A element; /* Reference or pointer to the next node in the chain, the reference is parameterized */ Node nextNode; // No argument Constructor public Node() { element = null; nextNode = null; } /* Constructor that takes two arguments, the element and reference to the next node */ public Node(A elem, Node n) { element = elem; nextNode = n; } //Setter method for the element member variable public void setElement(A elem) { element = elem; } //Getter method for the element member variable public A getElement() { return element; } //Setter method for the Node member variable public void setNextNode(Node n) { nextNode = n; } //Getter method for the Node member variable public Node getNextNode() { return nextNode; }}//The generic CircularList classpublic class CircularList{ //reference to the head node protected Node header; //reference to the tail node protected Node trailer; //reference to the current node protected Node cursor; //reference to the traveling node protected Node traverser; protected int size; public CircularList() { header = new Node(); trailer = new Node(); trailer.setNextNode(header); cursor = null; size = 0;

    Java ReferencePoint Suite 4

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • } public int getSize() { return size; } public boolean isEmpty() { return (size == 0); } //adds an element to the end of the list public void append(A elem) { Node temp = new Node(); temp.setElement(elem); temp.setNextNode(trailer); if(isEmpty()) { header.setNextNode(temp); }else { cursor.setNextNode(temp); } cursor = temp; size++; } //returns the first node in the list. It does not return header public Node getFirstNode() { return header; } /* Sets the traveling node to the header. Used when traversing list */ public void setTraverserToTop() { traverser = header; } //Returns the last node of the list. public Node getLastNode() { return trailer; } /* Returns the next node in the list from the current position of the traveling node */ public Node getNext() { traverser = traverser.getNextNode(); if(traverser == header) { traverser = traverser.getNextNode(); }else if(traverser == trailer) { traverser = traverser.getNextNode().getNextNode(); } return traverser; }}

    In the above code, the Node class represents a single node in the list. A Node object contains areference to next node in the linked list and a reference to the element that it contains. You canspecify the parameter type of the element, which the node can contain, when creating an instanceof the Node class.

    The CircularList class represents a circularly linked list. The CircularList class contains references tofour Node objects, header, trailer, cursor, and traverser. The header object represents the beginningof the list and the trailer object represents the end of the list. The cursor object represents theposition where the class would add the next element and the traverser object helps in traversing thelist. You can specify the parameter type of the CircularList class when instantiating the class.

    Java ReferencePoint Suite 5

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • A program called Animator uses this CircularList class. The Animator program displays ananimation created by displaying few still images successively. Listing 192 shows the completecode for the Animator.java program:Listing 192: The Animator.java Program

    import java.io.*;import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.net.*;public class Animator extends JApplet implements Runnable{ Image img; CircularList cl; Thread thread; String imageName = "Anim/anim"; String extension = ".jpg"; MediaTracker mt; public Animator() { /* Creating a CircularList object that stores image objects */ cl = new CircularList(); thread = new Thread(this); mt = new MediaTracker(this); } public void init() { try{ URL docBase = getCodeBase(); String url = docBase.toString() + imageName; for(int i = 1; i

  • In the above code, the Animator class extends the JApplet class and implements the Runnableinterface. This class reads images from the code base of the applet and stores them in aCircularList object. The applet uses a MediaTracker object to read these images from theCircularList object. The applet then traverses the CircularList object in an infinite loop to display theanimation.

    The CircularList object includes a parameter of type Image. This means only objects of type Imagecan be stored within the CircularList object. The thread object invokes the paint() method once inevery 50 milliseconds. The paint() method retrieves images from the CircularList object and paintsthem on the screen. You do not have to add code that typecasts the object retrieved from the listinto an image. You use the objects directly.Note To run this program, you should install JDK1.5 on your system. You can download the

    JDK1.5 Beta from http://java.sun.com/j2se/1.5.0/download.jsp.

    Compile all the class files using the javac command from the command line. Use a browser thatsupports Java to view the applet. Figure 191 shows the Animator applet displaying the animation:

    Figure 191: The Animator Applet Program

    Java ReferencePoint Suite 7

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • Using Generics with InterfacesSimilar to classes, interfaces can also be generic. The syntax for declaring a generic interface is thesame as that of a class. Interfaces contain only abstract methods. As a result, a class that inheritsfrom an interface provides implementation to methods declared in the interface. When methods inan interface receive or return objects of type Object, you need to typecast these objects toappropriate types to use them. Using generics, you can avoid this typecasting code by specifyingparameter type for the interfaces.

    For example, the List interface in the collections framework of JDK1.4 consists of the iterator()method. A class that implements the List interface should provide an implementation to the iterator()method. The Iterator object returned by this implementation is of type Object. To use this object youshould provide typecasting code to convert this object into an object of type Iterator.To avoid typecasting and simplify coding, you can enhance interfaces with parametric information.Listing 193 shows the List interface in the collection framework:

    Listing 193: The TestListClient Class

    class TestListClient{ public static void main(String args[]) { ArrayList al = new ArrayList(); tl.add("ABC"); tl.add("XYZ"); tl.add("PQR"); //iterate over the nodes Iterator i = al.iterator(); while(i.hasNext()) { String temp = (String) i.next(); System.out.println(temp); } }}

    The above code uses typecasting inside a while loop. The objects contained in the ArrayList objectare strings, but you need to typecast these objects to use them. You can avoid such typecasting, ifyou implement generic interfaces.

    Implementing Generic InterfacesGeneric interfaces, similar to generic classes, are useful when used with data structures andcontainer classes. For example, you can develop a circular linked list that uses generic interfaces.Listing 194 shows two interfaces, CustomList and Traverser:

    Listing 194: The CustomList and Traverser Interfaces

    public interface CustomList { public void append(A x); public Traverser getTraverser();}public interface Traverser{ public void setTraverserToTop(); public Node getNext();}

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • The above code defines two interfaces, CustomList and Traverser. These two interfaces are similarto List and Iterator interfaces of the collection framework.

    You can develop a CircularList class that implements the interfaces, CustomList and Traverser. TheCircularList class uses the Node class that represents the nodes of the CircularList object. Listing195 shows the code for the CircularList class and the Node Class:Listing 195: The CircularList and Node Classes

    class Node{ A element; Node nextNode; public Node() { element = null; nextNode = null; } public Node(A elem, Node n) { element = elem; nextNode = n; } public void setElement(A elem) { element = elem; } public A getElement() { return element; } public void setNextNode(Node n) { nextNode = n; } public Node getNextNode() { return nextNode; }}public class CircularList implements CustomList, Traverser{ protected Node header; protected Node trailer; protected Node cursor; protected Node traverser; protected int size; public CircularList() { header = new Node(); trailer = new Node(); trailer.setNextNode(header); cursor = null; size = 0; } public int getSize() { return size; } public boolean isEmpty() { return (size == 0); } public void append(A elem) { Node temp = new Node(); temp.setElement(elem); temp.setNextNode(trailer); if(isEmpty()) { header.setNextNode(temp); }else {

    Java ReferencePoint Suite 9

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • cursor.setNextNode(temp); } cursor = temp; size++; } public Node getFirstNode() { return header; } public void setTraverserToTop() { traverser = header; } public Node getLastNode() { return trailer; } public Node getNext() { traverser = traverser.getNextNode(); if(traverser == header) { traverser = traverser.getNextNode(); }else if(traverser == trailer) { traverser = traverser.getNextNode().getNextNode(); } return traverser; } public Traverser getTraverser() { return this; }}

    In the above code, the CircularList class implements the Traverser and CustomList interfaces. Themethods in these interfaces, append(A x), getTraverser(), setTraverserToTop(), and getNext(), areoverridden in the CircularList class.

    The ParametrizedInterfaceTest program uses the CircularList class and the generic interfaces, asshown in Listing 196:Listing 196: The ParametrizedInterfaceTest Program

    public class ParametrizedInterfaceTest{ public static void main(String[] args) { CircularList intList = new CircularList(); CircularList stringList = new CircularList(); //populate integer list for(int i = 0; i

  • Traverser strTraverser = stringList.getTraverser(); strTraverser.setTraverserToTop(); System.out.println("Traversing String List"); for(int i = 0; i
  • The above snippet adds two String objects and an Integer object to a Vector object. However, theVector object recognizes the string and Integer objects as Object objects. You can add objects withdifferent data types into a single collection object in JDK 1.4.In generics, subtyping is not covariant but is invariant. When subtyping is invariant, a subclassobject is not a type of its superclass object. Listing 198 shows an example of subtyping ingenerics:

    Listing 198: Subtyping in Generics

    Vector strVec = new Vector();String name = "Eric";String city = "Boston";Integer age = new Integer (25);strVec.add(name);strVec.add(city);Vector objVec = strVec;ObjVec.add(age);

    The above snippet does not compile in JDK 1.5 with generics. The line Vector objVec =strVec throws a compile time error message. This means in Java with generics a Vector of Stringsis not a Vector of Objects. You can generalize this by stating that if ABCD is a class, XYZ is asubclass of ABCD, and E is a generic type declaration involving ABCD, then E is not asubtype of E. The invariance of types is applicable only to generic types and normal classsubtypes are still covariant in JDK 1.5:

    Irrespective of the type enclosed by a generic class, you cannot differentiate invocations to thisclass that are generic, based on their parameter types. A Vector.getClass() returns thesame value as the method Vector.getClass(). The class information, obtained at runtimefor all objects of a generic class, is the same. This is because, at the time of compilation, thecompiler erases all generic information in the source code. As a result, the compiled bytecodes arecompletely compatible with the older version of JRE and generic class behaves the sameregardless of the type that may be associated with it.

    In JDK 1.5, it is not possible to invoke the instanceof operator on an object of a generic class, toquery about its type. The compiled bytecode does not retain parameter type information of genericclasses, therefore at runtime generic classes do not exist. The instanceof operator verifies if anobject belongs to a particular class at runtime. As a result, calling the instanceof operator with ageneric class throws a compile time error. For example, the following code throws a compile timeexception.Vector strings = new Vector();if(strings instanceof Vector)

    You should avoid typecasting of generic code. At runtime, all generic code is converted into normalcode. As a result, there does not exist any generic type information which you can typecast. Suchtypecasting code raises an unchecked warning at compile time.

    In addition, you cannot create arrays of generic types. This generates a compile time error.However, you can declare arrays, which are generic.

    Java ReferencePoint Suite 12

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • Using WildcardsThe methods of the classes in the collections framework of JDK1.4 always receive and return anobject of type Object. This allows you to share elements of one collection with other collections. Thismay not be directly possible when using generic collections. This is because, in generic collections,an object of type Collection is not the same as an object of type Collection. TheCollection type is an invariant collection of Objects.To overcome the problem of invariance in objects, generics introduces bounding or constraining oftype parameters. A constraint, or a bound, is created using wildcards in generics.

    Working with WildcardsIn a payroll program that calculates the pay of the employees, you can group all the Employeeobjects into a Collection object. You can use a class hierarchy for the employees where a Managerclass, Executive class, and Clerk class extends the Employee class.

    If you code a method that takes a Collection object as an argument, then this method works inJDK1.4 but not in JDK 1.5. This is because of the invariance caused by generics. Listing 199shows a pseudocode that prints the salary of employees:Listing 199: The Pseudocode for Payroll Program

    public abstract class Employee{ public abstract double calculatePay();}public class Manager extends Employee{ public double calculatePay() { //calculate pay for managers here }}public class Executive extends Employee{ public double calculatePay() { //calculate pay for executives here }}public class Clerk extends Employee{ public double calculatePay() { //calculate pay for clerks here }}public class Payroll{ public static void main(String args[]) { ArrayList managerList = new ArrayList(); managerList.add(new Manager(4000, 1500, 800, 500, 1500)); managerList.add(new Manager(4200, 1500, 800, 500, 1700)); printPay(); } static void printPay(Collection e) { While(e.hasNext()) { System.out.println(e.next().calculatePay()); } }}

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • In the above code, the printPay() method in the Payroll class fails due to the invariance of generics.if you specify the parameter type as Collection, the method fails.JDK 1.5 introduces the notation, which represents an unknown type. Using this notation, youwrite a collection of unknown type as Collection. You can use this notation to represent acollection of any type. You can also specify the upper and lower bounds to the unknown type.These bounds provide information about the hierarchy of the unknown type to the compiler. Thenotation
  • entertainmentAllowance = 0; commissions = 0; } public Manager(double basic, double deductions, double travelAllowance, double entertainmentAllowance, double commissions) { super(basic, deductions); this.travelAllowance = travelAllowance; this.entertainmentAllowance = entertainmentAllowance; this.commissions = commissions; } public void setTravelAllowance(double ta) { travelAllowance = ta; } public void setEntertainmentAllowance(double ea) { entertainmentAllowance = ea; } public void setCommissions(double com) { commissions = com; } public double getTravelAllowance() { return travelAllowance; } public double getEntertainmentAllowance() { return entertainmentAllowance; } public double getCommissions() { return commissions; } public double calculatePay() { return ((getBasic() + travelAllowance + entertainmentAllowance + commissions) getDeductions()); }}public class Clerk extends Employee{ double overtimePay; public Clerk() { super(); overtimePay = 0; } public Clerk(double basic, double deductions, double overtimePay) { super(basic, deductions); this.overtimePay = overtimePay; } public void setOvertimePay(double otp) { overtimePay = otp; } public double getOvertimePay() { return overtimePay; } public double calculatePay() { return ((getBasic() + overtimePay) getDeductions()); }}

    In the above code, the Manager and Clerk classes are the subclasses of the Employee class.These classes override the claculatePay() method in the Employee class. The managers salaryincludes components that are not a part of the clerks salary. The Payroll program uses the

    Java ReferencePoint Suite 15

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • Employee class and its subclasses to represent the collective payroll information of all employees.This program uses a collection object to store information about all the employees and process theirsalaries. Listing 1912 shows the code for the Payroll program:Listing 1912: The Payroll Program

    import java.util.*;public class Payroll{ public static void main(String args[]) { ArrayList managerList = new ArrayList(); managerList.add(new Manager(4000, 1500, 800, 500, 1500)); managerList.add(new Manager(4200, 1500, 800, 500, 1700)); managerList.add(new Manager(4000, 1500, 800, 500, 1800)); managerList.add(new Manager(4300, 1500, 800, 500, 1600)); System.out.println("Pay of managers"); printPay(managerList); ArrayList clerkList = new ArrayList(); clerkList.add(new Clerk(2500, 500, 1000)); clerkList.add(new Clerk(2700, 500, 1200)); clerkList.add(new Clerk(2400, 500, 1300)); clerkList.add(new Clerk(2800, 500, 0)); System.out.println("Pay of clerks"); printPay(clerkList); } public static void printPay(List

  • Using Generic MethodsSimilar to classes and interfaces, methods can also be generic. In Java, any type declaration suchas variables, methods, classes, and interfaces can be generic. For example, a routine such as aswap function can be generic. As a result, you can implement a number of swap function variants,such as swapping one element or swapping a range in a data structure.

    It is possible to use wildcards and generic methods interchangeably. However, you should preferwildcards when you want covariance along with strong typing, while using container classes.

    Generic methods are useful when there is a type dependency between the arguments and thereturn types of those arguments. You can use wildcards with declaration of any type in Java. Youcan also use wildcards with method signatures.

    Modify the Payroll program to include a generic method that adds the two employee lists to acommon list. Listing 1913 shows the modified version of the Payroll program, called Payroll2:Listing 1913: Payroll2 Program

    import java.util.*;public class Payroll2{ public static void main(String args[]) { ArrayList managerList = new ArrayList(); managerList.add(new Manager(4000, 1500, 800, 500, 1500)); managerList.add(new Manager(4200, 1500, 800, 500, 1700)); managerList.add(new Manager(4000, 1500, 800, 500, 1800)); managerList.add(new Manager(4300, 1500, 800, 500, 1600)); ArrayList clerkList = new ArrayList(); clerkList.add(new Clerk(2500, 500, 1000)); clerkList.add(new Clerk(2700, 500, 1200)); clerkList.add(new Clerk(2400, 500, 1300)); clerkList.add(new Clerk(2800, 500, 0)); ArrayList empList = new ArrayList(); mergeLists(empList, managerList); mergeLists(empList, clerkList); System.out.println("Pay of All Employees"); printPay(empList); } public static void mergeLists(ArrayList e1, ArrayList

  • Figure 194: The Payroll2 Program

    Java ReferencePoint Suite 18

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

  • Related TopicsFor related information on this topic, you can refer to:

    Understanding Classes and Objects in Java

    Event and Exception Handling in Java

    Reprinted for v697039, Verizon SkillSoft, SkillSoft Corporation (c) 2002, Copying Prohibited

    Table of Contents Point 9: Working with Java Generics Introducing Generics Overview of Generic Classes Implementing Generic Classes

    Using Generics with Interfaces Implementing Generic Interfaces Subtyping and Generics

    Using Wildcards Working with Wildcards Using Generic Methods

    Related Topics