Anda di halaman 1dari 32

A

Appendix: Generic Types


Generic types are not a different capability from the class and interface types that we have undergone in the earlier chapters. The facility for defining generic class and interface types is an extension of the ordinary definition of classes and interfaces that we are already familiar with that enables you to define families of classes and interfaces. Using Generics it is possible to create classes, interfaces, and methods that will work in a type-safe manner with various kinds of data. With generics you can define an algorithm only once and then apply that algorithm to a wide variety of data types without any additional effort. This chapter can be considered as the leading edge of the Object-Oriented Programming. You can get by without knowing any of the information in this chapter, so feel free to skip it if you are on your way to something more interesting. It is better to just have an idea about the collections so as to learn how ArrayList and LinkedList classes to use the new generics feature. Collection Framework, an important feature in Java, has been significantly affected by generics. Whereas collection is a group of objects, Collections Framework defines several classes, such as lists and maps that manage collections. How generics have affected collections classes is that they can be used with complete type safety. Overall generics enabled an existing feature to be substantially improved. Lets see an example, the add method for the ArrayList class had this declaration, public boolean add(Object obj), in which you can pass any type of object to the add method which would be accepted by the array list. Before retrieving any item from the collection, there is a definite need of casting it to the correct object type. Suppose if you had an array list named custlist with Customer objects, following line of code can be used in order to get the first customer from the list:
Customer c = (Customer) custlist.get(obj);

Since, the add method can accept any object type, its not certain that only certain types of objects could be added to the collection. So, generics were invented. Now you can declare the ArrayList like this:
ArrayList<Customer> custlist = new ArrayList<Customer>();

Here, custList is declared as an ArrayList which will hold only Customer types. The add method can be declared as public boolean add(Customer c).

Appendix A

Now, you can only add employee objects to the list. And the get method will be declared as public Customer get(int i). The get method here is returning a Customer object. So there is no need to cast the result of the Customer as the compiler already knows that the object is Employee.

What are Generic Types?


A generic type, also referred to as a parameterized type, is a class or interface type definition consisting of one or more type parameters. This is one of the several extensions to the Java programming language. It was first introduced in JDK 1.5. With generics, it is no longer necessary to explicitly employ casts to translate between object and the type of data that is actually operated upon. With generics, all casts are automatic and implicit. In other words, generics expand your ability to reuse code. You can define an actual class or interface type from a generic type by providing a type argument for each of the type of parameters that the generic type has. Heres a typical usage of that kind:
List Lst = new LinkedList(); Lst.add(new Integer(0)); Integer i = (Integer) Lst.iterator().next();

The cast on third line is slightly misinterpreting. The programmer knows exactly, what kind of data have been placed into a particular list. However, the cast is essential. The compiler can only guarantee you that an object will be returned by the iterator. To guarantee you that the assignment to a variable of type Integer is type safe, the cast is essential. It not only introduces clutter but also introduces the possibility of a runtime error, since the programmer might be mistaken. What will happen if programmers could actually express their intentions, and mark out a list which is restricted to contain a particular data type? This becomes the core idea behind generics. Heres a version of the preceding-mentioned program fragment using generics:
List<Integer> Lst = new LinkedList<Integer>(); Lst.add(new Integer(0)); Integer in = Lst.iterator().next();

Note the type declaration for the variable Lst. It specifies that it is not just an arbitrary List, but also a list of Integer, written List<Integer>. Lets say that List is a generic interface that takes a type parameterin this case, Integer. When creating the list object, we also specify a type parameter. The other thing to notice is movement of the cast to third line. Now, you may think that all we have achieved is to move the clutter around. We have Integer as a type parameter on first line instead of a cast to Integer in third line. However, we can notice a very big difference here. The compiler can now check the type correctness of the program at compile-time. When we say that Lst is declared with type List<Integer>, this explains us something about the variable Lst, which holds true wherever and whenever it is used and the compiler will ensure it. In comparison, the cast tells us something the programmer thinks is true at a single point in the code. The net output, especially in large programs, is improved readability and robustness.

Generic Types

Look at this statement:


List<int> Lst = new List<int>(53);

This statement generates an error (Error cant use primitive type) because while declaring an instance of a generic type, the type argument passed to the type parameter must be a class type. You cannot use a primitive type, such as int or char. With List it is possible to pass any class type Integer, but you cannot pass a primitive type. Instead you can also use the type wrappers to encapsulate a primitive type. All instances of a generic class, regardless of what type each instance has been parameterized with, are considered to be the same class. A type variable is an unqualified identifier that is used in the definition of a generic class as a placeholder. Type variables appear between the angle braces. This identifier will be replaced (automatically) by whatever specific object type the user of the generic class plugs into the generic class. Take this example:
List<Integer> Lst; . . . List<String> str; Lst = str;

This is wrong because a reference of one specific version of a generic type is not type compatible with another version of the same generic type. Even though Lst and str are of type List<>, they are references to different types as their type parameters are different. The Java compiler does not actually create different versions of a generic class. What the compiler does is that it removes all generic type information, substituting the necessary casts, to make the code behave as if a specific version of List has been created. In reality there is only one version of List that actually exists in your program. The process of removing generic type information is called erasure.

Parameterized Types
A generic class is also known as a parameterized class. Parameterized types enable you to create classes, interfaces, and methods in which the type of data upon which they operate is specified as a parameter. Using generics, it is possible to create a single class that automatically works with different types. The class is defined with space for one or more parameters, placed between the angle braces, where the type of the parameters is specified during the declaration of a specific instance of the class. For the rest of this section, the term generic class will be used to refer to a parameterized class. Also note that a class or an interface in Java can be made generic. For the rest of this section, unless otherwise stated, the word class includes classes and interfaces. In the example at the start of this section, Integer is the specific type that takes the place of the type variable for the parameterized ArrayList. The direct super-types of a generic class are the classes in the extends clause, if present (or java.lang.Object if not present), and any interfaces, if any are present. Therefore, in the following example, the direct super-type is ArrayList:
class arrlist<ItemType> extends ArrayList { }

The super-types of type variables are those listed in the bounds list for that type variable. If none are specified, java.lang.Object is the super-type. In hierarchies of generic classes, one important restriction exists. To support translation by type erasure (see here for more on type erasure), a class or

Appendix A

type variable cannot have two different parameterizations of the same class/interface at the same time. This is an example of an illegal hierarchy:
interface BaseInterface<A> { A getInfo(); } class app implements BaseInterface<Integer> { public Integer getInfo() { return(null); } } class Childapp extends app implements BaseInterface<String> { }

The interface BaseInterface is first parameterized with Integer, and later parameterized with String. These are in direct conflict, so the compiler will issue the following error:
c:\code\BadParents.java:14: BaseInterface cannot be inherited with different arguments: <java.lang.String> and <java.lang.Integer> class Childapp extends app implements BaseInterface<String> { } 1 error

Defining Generic Classes


As mentioned earlier, both interfaces and classes can be parameterized. Since type variables have no inherent type, all that matters is the number of type variables that act as parameters in a class. The list of type variables appears between the angle braces (the less-than sign and greater-than sign). An example of changing the existing ArrayList class from a nongeneric class to a generic class changes its signature to:
public class ArrayList<ItemType> { . . . }

The type variable here is ItemType, and can be used throughout the class as a not-yet-specified type. When an object of the class is defined, a type is specified and is plugged into the generic class by the compiler. The scope of a type variable extends throughout the class, including the bounds of the type parameter list, but not including static members/methods. Each type variable can also have bounds that place a restriction on the type variable. The type variable can be forced to extend from a class other than java.lang.Object (which it does when no extends clause is specified) or implement any number of specific interfaces. For example, if you define an interface GraphicContext as part of a graphics library, you might write a specialization of a collection to only hold objects that implement the GraphicContext interface. To place only an interface restriction on the type variable, the extends clause must be specified, even if it is only java.lang.Object. However it is possible to only list interfaces after the extends clause. If you only list interfaces, it is implicitly understood that java.lang.Object is the base class of the type variable. Note that interfaces are separated by the ampersand (&). Any number of interfaces can be specified.

Casts and InstanceOf


Another implication of sharing of a generic class among all its instances, is that it usually makes no sense to ask an instance if it is an instance of a particular invocation of a generic type:

Generic Types

Collection col = new ArrayList<String>(); if (col instanceof Collection<String>) { . . . }

In the similar way, a cast such as:


Collection<String> colstring = (Collection<String>) colec;

It gives an unchecked warning, since this isnt something, which the runtime system is going to check for you. The same is true of type variables:
<T> T badCast(T t, Object ob) { return (T) ob; }

Type variables dont exist at runtime meaning that they entail no performance overhead in either time or space, which is a correct thing. Unfortunately, it also means that you cant reliably use them in casts.

A Simple Program on Generics


Before going into the more complex details of this special generic class, lets take a simple example shown in the following code (you can find this file, genfeature.java on CD) in order to understand the concept of Generics:
class defgen<G> { G obj; defgen(G ob) { obj=ob; } G getobj() { return obj; } void displaytype() { System.out.println("G is having following type :- "+ obj.getClass().getName()); } } class genfeature { public static void main(String args[]) { defgen<Integer> iogen; iogen=new defgen<Integer>(100); iogen.displaytype(); int val=iogen.getobj(); System.out.println("value is equal to - "+val); System.out.println (); } }

Here, G is the parameter passed to the generic class defgen. So, G which is defined between the brackets will be called as per the requirement of the programmer. Obj is the object of the type G means the data type of the obj will be the same as that of G. The constructor defgen(G ob) has the type G, which emphasize that the ob will also possess the same data type that of G. One thing here to stress that the primitive data types are not allowed to pass through the generic classes, although you can use the Wrapper class to use data types in order to access the integer or string values using the feature of autoboxing described earlier. The function G getobj() is returning a value of G type object. As obj is also defined of type G. The displaytype() method displays the type of G by calling the methods getName() and getClass()

Appendix A

on the object obj. The getClass() method returns the type of class and getName() method returns the string representation of the class name. Now, comes the explanation of the main() through which the methods are used. We have created an object iogen of the type defgen defined preceding and the Integer data type of the wrapper class is being passed in order to access the integer values as objects. The iogen is being initialized with any integer value (here 100) using the new keyword to allocate the memory. The methods getobj() and displaytype() are called using the object iogen. The values passed are assigned to the variable val which is then displayed. Here we have not used any type cast in order to fetch the value of the object. The output of the preceding program is:
G is having following type :- java.lang.Integer value is equal to 100

Bounded Generic Type and Wildcards


Suppose you want to create a generic class that contains a method that returns the average of an array of any type of numbers, including integers, floats, and doubles. Lets look at this example which gives a compilation error just because the compiler has no way of knowing that you are passing only numeric types:
class GT<G> { G[] bnd; GT(G[] t) { bnd = t; } double aver() { double sum = 0.0; for(int v=0; v < bnd.length; v++) { sum += bnd[v].dblValue(); // Error!!! return sum / bnd.length; } } }

To handle such situations, Java provides bounded types. When specifying a type parameter you can create an upper bound that declares the superclass from which all type arguments must be derived. This can be done using extends clause. How will extends solve the problem found earlier? You can use Number as an upper bound in order to make the Java compiler know all objects of type G that call dblValue(). See the following code to understand better (you can find this file, BndDemo.java on CD):
class GT<G extends Number> { G[] bnd; GT(G[] t) { bnd = t; } double aver() { double sum = 0.0; for(int v=0; v < bnd.length; v++) sum += bnd[v].doubleValue(); return sum / bnd.length; } } class BndDemo { public static void main(String args[]) {

Generic Types Integer vnums[] = { 10, 20, 30, 40, 50 }; GT<Integer> vob = new GT<Integer>(vnums); double j = vob.aver(); System.out.println("vob average is " + j); Double dnums[] = { 10.1, 20.2, 30.3, 40.4, 50.5 }; GT<Double> dob = new GT<Double>(dnums); double k = dob.aver(); System.out.println("dob average is " + k); } }

Heres the output:


vob average is 30.0 dob average is 30.3

You can bound a generic type, for example, the showAll, showXY, showXYZ method to display the coordinates from the same entry. The bounded generic type specifies that E is a generic subtype of Coords.

An unbounded generic type <E> is same as <E extends Object>. Here is a small excerpt from the definitions of the interfaces List and Iterator in package java.util.

Take a look at this code to understand it better (you can find this file, gen_bound.java on CD):
class TwoDim { int x, y; TwoDim(int a, int b) { x = a; y=b; } } class ThreeDim extends TwoDim { int z; ThreeDim(int a, int b, int c) { super(a,b); z=c; } } class FourDim extends ThreeDim { int t; FourDim(int a, int b, int c, int d) { super(a,b,c); t=d; } } class Coords<T extends TwoDim> { T[] crds; Coords(T[] o) { crds = o; } } class gen_bound { static void displayXY(Coords<?> c) { System.out.println("X Y Coordinates:"); for(int i=0; i<c.crds.length; i++) System.out.println(c.crds[i].x+" " + c.crds[i].y); System.out.println(); }

Appendix A static void displayXYZ(Coords<? extends ThreeDim> c) { System.out.println("X Y Z Coordinates:"); for(int i=0; i<c.crds.length; i++) System.out.println(c.crds[i].x + " " + c.crds[i].y + " " + c.crds[i].z); System.out.println(); } static void displayAll(Coords<? extends FourDim> c) { System.out.println("X Y Z T Coordinates:"); for(int i=0; i<c.crds.length; i++) System.out.println(c.crds[i].x + " " + c.crds[i].y + " " + c.crds[i].z + " " + c.crds[i].t); System.out.println(); } public static void main(String args[]) { TwoDim tdcrd[]= { new TwoDim(0,0), new TwoDim(4,4), new TwoDim(12,7), new TwoDim(-5,-63) }; Coords<TwoDim> tdcrds = new Coords<TwoDim>(tdcrd); System.out.println("Contents of tdlocs."); displayXY(tdcrds); FourDim fdcrd[] = { new FourDim(1,2,3,4), new FourDim(6,5,14,5), new FourDim(3,4,5,6), new FourDim(4,-3,-23,27) }; Coords<FourDim> fdcrds = new Coords<FourDim>(fdcrd); System.out.println("Contents of fdlocs."); displayXY(fdcrds); displayXYZ(fdcrds); displayAll(fdcrds); } }

Heres the result of this code:


Contents of tdlocs. X Y Coordinates: 0 0 4 4 12 7 -5 -63 Contents of fdlocs. X Y Coordinates: 1 2 6 5 3 4 4 -3 X Y Z Coordinates: 1 2 3 6 5 14 3 4 5 4 -3 -23 X Y Z T Coordinates: 1 2 3 4 6 5 14 5

Generic Types 3 4 5 6 4 -3 -23 27

This should all be familiar, except for the stuff in angle brackets. Those values are the declarations of the formal type parameters of the interfaces List and Iterator. Throughout the generic declaration, type parameters can be used, where you would use ordinary types. In the introduction, we saw invocations of the generic type declaration List, such as List<Integer>. In the invocation (usually called a parameterized type), all occurrences of the formal type parameter (in this case, E) are replaced by the actual type argument (in this case, Integer). You may assume that List<Integer> stands for a version of List where E has been uniformly replaced by Integer:
public interface IntegerList { void add(Integer x) Iterator<Integer> iterator(); }

Though this intuition can be guiding, but its also misleading. It is helpful, because the parameterized type List<Integer> does indeed have methods that look just like this expansion. It is misleading because the declaration of a generic is actually never expanded in this way. Multiple copies of the code are not in source, binary, disk and not even in memory. If you are a C++ programmer, youll understand that this is very different than a C++ template. A generic type declaration is compiled once and for all, and turned into a single class file, just like an ordinary class or interface declaration. Type parameters are analogous to the ordinary parameters used in methods or constructors. A generic declaration has formal type parameters, in the same way like a method has formal value parameters that describe the kinds of values it operates on. When a method is invoked, actual arguments are substituted for the formal parameters, and the method body is evaluated whereas when a generic declaration is invoked, the actual type arguments are substituted for the formal type parameters.

We recommend that you use pithy (single character if possible) but still evocative names for formal type parameters. Its best to avoid lower three case characters in these names, thus, making it easy to differentiate formal type parameters from ordinary classes and interfaces. As in the preceding examples, many container types use E for element, in later examples, well see some additional conventions.

In order to make the upper bound for a wildcard type Object, just write it as:
? extends Object

Specifying a wildcard with an upper bound is very useful as it forces the specification to represent only class types and not interface types. This way you can use this when you want to specify any type as long as its a class type. A bounded wildcard is especially important when you are creating a generic type that will operate on a class hierarchy. You can explicitly constrain a wildcard specification. One possibility is to specify that it extends another type. This type of constraint is described as an upper bound of the wildcard type because it implies that any subclass of the type that the wildcard extends is acceptable, including the type itself. Take a look at this snippet:

Appendix A

class TD { int x, y; TD(int a, int b) { x = a; y = b; } }

The TD encapsulates a two-dimensional, XY coordinate. See a generic class called coorobj, which stores an array of coordinates:
class coorobj <G extends TD> { G[] coords; Coords (G[] g) { coords = g; } }

The Coords specifies a type parameter bounded by TD. This is an indication that any array stored in a Coords object will contain objects of type TD or one of its subclasses. Now to write a method that displays the X and Y coordinates for each in the coords array, the following can be done:
static void showXY(Coords<?> w) { System.out.println("2-D Coordinates:"); for(int k=0; k < w.coords.length; k++) System.out.println(c.coords[k].x + " " + w.coords[k].y); System.out.println(); }

Generics and Subtyping


Lets now test our knowledge of generics.
List<String> lstring = new ArrayList<String>(); List<Object> lobject = lstring;

Here Line 1 is certainly legal. The trickier part of the question is Line 2. This boils down to the question: Is a list of String, a list of Object? Most peoples instinct is to answer: sure!. Well, take a look at the next few lines:
lobject.add(new Object()); String ls = lstring.get(0);

Here we have aliased lstring and lobject. Accessing lstring, a list of String, through the alias lobject, we can insert arbitrary objects into it. As a result, lstring does not hold just Strings anymore, and when we try and get something out of it, we get a rude surprise. Of course, the Java compiler will prevent this from happening. Line 2 will cause a compile time error. This probably is the hardest thing you need to learn about generics, because it goes against our deeply held intuitions. The reason for this is the assumption that collections dont change. Our instinct takes these things to be immutable.

Generic Types

For example, if the department of motor vehicles provides a list of drivers to the census bureau, it sounds reasonable. We think that a List<Driver> is a List<Person>, assuming that Driver is a subtype of Person. In fact, what is being passed is a copy of the registry of drivers. Otherwise, the census bureau could corrupt the DMVs records by adding new people who are not drivers into the list. In order to tackle this situation, its advisable to consider more flexible generic types. The rules we have seen so far have limitations. Look into this example to understand this concept better (you can find this file, Subclas.java on CD):
class Mainclas { int mnum; Mainclas(int j) { mnum = j; } int getnum() { return mnum; } } class Gensub<T> extends Mainclas { T item; Gensub(T k, int l) { super(l); item = k; } T getitem() { return item; } } class Subclas { public static void main(String args[]) { Gensub<String> h = new Gensub<String>("Black Book", 10); System.out.print(h.getitem() + " "); System.out.println(h.getnum()); } }

Heres the output:


Black Book 10

Type Parameters
The generic type LinkedList<T> has a single type parameter; normally you can define a generic type with as many type parameters as you want to. For instance, if you want to define a generic type which, defines a set of classes that encapsulates a pair of objects of arbitrary types. This would typically arise where one object is used to access the other in a collection. For example, you might store Person objects in a collection that encapsulates personal details such as the name, the address, and the phone number. You could associate each Person object with a Name object that you use as a key to retrieve the Person object. One way of establishing the association between an object and its key would be to encapsulate both in another objectof type Pair, say. How can you declare more then one type parameter in generic type? Simply use a comma-separated list. Heres the way by which we can define a generic type, Pair<K,V>, that is to be used for defining classes that encapsulate a key/value pair of any type:
public class app <KeyType, ValueType> { public app(KeyType key, ValueType aValue) { key = aKey;

Appendix A value = aValue; } public getKey() { return key; } public getValue() { return value; } public setValue(ValueType aValue) { value = aValue; } private KeyType key; private ValueType value; }

It is quite obvious that a practical definition would be more complicated than thisyoud need a means of comparing key objects. Take a look at this practical example that uses two type parameters (you can find this file, SimpGen.java on CD):
class TwoPara<G, T> { G ty1; T ty2; TwoPara (G g1, T g2) { ty1 = g1; ty2 = g2; } void DispTypes() { System.out.println("G is " + ty1.getClass().getName()); System.out.println("T is " + ty2.getClass().getName()); } G getty1() { return ty1; } T getty2() { return ty2; } } class SimpGen { public static void main(String args[]) { TwoPara<Integer, String> pgObj = new TwoPara<Integer, String>(2005, "Parameters"); pgObj.DispTypes(); int i = pgObj.getty1(); System.out.println("The value with integer = " + i); String str = pgObj.getty2(); System.out.println("The value with string: " + str); } }

Heres the output:


G is java.lang.Integer T is java.lang.String The value with integer = 2005 The value with string: Parameters

Variables of Raw Types


You may use a generic class without specifying a concrete type in the following way:
app stack = new app(); This is roughly equivalent to app<Object> stack = new app<Object>();

Generic Types

You can use the generic type name by itself to define variables. The class that corresponds to the raw type is produced by removing the type parameters from the generic type definition and replacing each instance of a type variable in the definition by the leftmost bound of its correspond type parameter. This process of mapping from a generic type to non-generic type is called type erasure because all occurrence of a type variable are effectively erased from the generic class definition. A raw type exists as a consequence of implementing generic types using type erasure. In the absence of any explicit type parameter bound, every type parameter T is implicitly bounded by type object. All occurrence of T in a generic type definition will be replaced by object to produce the raw type. This is important for interface type such as iterable<> and comparable<> in the standard package. Interface in the standard package that defined methods are generally defined as generic type for maximum flexibility. When you implement such interface in an ordinary class without specifying a type argument, your class is implementing the raw type, so the method in the interface will be declared with parameters and/ or return type of type object.

Backward Compatibility
GenericStack class used without a type parameter is called a raw type. The raw type is allowed for backward compatibility with the prior versions of JDK. For example, generic type is used in java.lang.Comparable, but a lot of code is still using the raw type Comparable as shown here:
public class app { public static Comparable max(Comparable com1, Comparable com2) { if (com1.compareTo(com2) > 0) { return com1; } else { return com2; } }

Here Comparable com1 and Comparable com2 are raw type declarations. Raw type is unsafe. For example, you may invoke the max method using:
app.max("Welcome", 23); A runtime error will be shown because you cant compare a string with an integer object. The compiler displays a warning on Line 5 when compiled with the option Xlint:unchecked

A better way to write the max method is to use a generic type, as shown in next code. If you invoke the max method using:
app1.max("Welcome", 23);

It will give you a compilation error because in app1 two arguments of the max method must have the same type (e.g. two strings or two integer objects):
public class app1 { public static <E extends Comparable<E>> E max(E com1, E com2) { if (com1.compareTo(com2) > 0) { return com1; } else { return com2; } }

Appendix A

For example, you may declare a raw type stack in Line 1, assign new app<String> to it in Line 2, and push a string and an integer object to the stack in Lines 3 and 4:
app stack; stack = new app<String>(); stack.push("Welcome to Java"); stack.push(new Integer(2));

Line 4 is usually unsafe because here the stack is supposed to store String, but an Integer object is added into the stack. Line 3 should be OK, but the compiler will show warnings on both Lines 3 and 4, as it cant follow the semantic meaning of the program. What the compiler knows is that stack is a raw type and it is unsafe to perform certain operations. Therefore, warnings are shown to alert potential programs.

Wildcards
We can define a particular type from the set defined by a generic type by supplying a type argument for each of its type parameter. To understand this lets take an example, to specify the BinaryTree<> type that stores objects of type string, you specify the type argument as string so the type is BinaryTree<String>. Instead of supplying a specific type as the type argument for the generic type, you can specify the argument as ? (wildcard), in which case you have specified the type argument as a Wildcard. A wildcard represents any class or interface type. You can declare variable of a generic type by using a wildcard type argument. For example:
BinaryTree <?> bt = new BinaryTree<Double>();

Here well consider some of the more advanced uses of wildcards. We have seen how wildcards were useful when reading from a data structure. Now consider the inverse, a write-only data structure. The interface Sink is a simple example of this sort:
interface Sink<T> { flush(T t); }

We can imagine using it as demonstrated by the code here. The method writeAll() is designed to flush all elements of the collection col to the sink snk, and return the last element flushed:
public static <T> T writeAll(Collection<T> col, Sink<T> snk) { T last; for (T t : col) { last = t; snk.flush(last); } return last; } . . . Sink<Object> s; Collection<String> collec; String strng = writeAll(collec, s);

Generic Types

As written, the call to writeAll() is illegal, as no valid type argument can be inferred; neither String nor Object are appropriate types for T, because the Collection element and the Sink element must be of the same type. We can fix this by modifying the signature of writeAll() as shown here, using a wildcard:
public static <T> T writeAll(Collection<? extends T>, Sink<T>){...} ... String sting = writeAll(cs, s); // call ok, but wrong return type

The call is now legal, but the assignment is erroneous, since the return type inferred is Object because T matches the element type of s, which is Object. The solution is to use a form of bounded wildcard we havent seen yet: wildcards with a lower bound. The syntax ? super T denotes an unknown type that is a super type of T. It is the dual of the bounded wildcards we have been using, where we use ? which extends T to denote an unknown type that is a subtype of T:
public static <T> T writeAll(Collection<T> col, Sink<? super T> snk){...} ... String srng = writeAll(cs, s); // Yes!

Using this syntax, the call is legal, and the inferred type is String, as desired. Remember, the supertype relation is reflexive. Now lets turn to a more realistic example. A java.util.TreeSet<E> represents a tree of elements of type E that are ordered. One way to construct a TreeSet is to pass a Comparator object to the constructor. That comparator will be used to sort the elements of the TreeSet according to a desired ordering:
TreeSet(Comparator<E> c) interface Comparator<T> { int compare(T fst, T snd); }

Here the Comparator interface is essential.

Suppose we want to create a TreeSet<String> and pass in a suitable comparator. We need to pass it a Comparator that can compare Strings. This can be done by a Comparator<String>, but a Comparator<Object> will do just as well. However, we wont be able to invoke the constructor given preceding on a Comparator<Object>. We can use a lower bounded wildcard to get the flexibility we want:
TreeSet(Comparator<? super E> c)

This allows any applicable comparator to be used. As a final example of using lower bounded wildcards, lets look at the method Collections.max(), which returns the maximal element in a collection passed to it as an argument. Now, in order for max() to work, all elements of the collection being passed in must implement Comparable. Furthermore, they must all be comparable to each other. A first attempt at generifying this method signature yields:

Appendix A

public static <T extends Comparable<T>> T max(Collection<T> coll)

That is, the method takes a collection of some type T that is comparable to it, and returns an element of that type. This turns out to be too restrictive. To see why, consider a type that is comparable to arbitrary objects:
class trial implements Comparable<Object> { . . . } . . . Collection<trial> coltr = . . .; Collections.max(coltr); // should work

Every element of coltr is comparable to every other element in coltr, since every such element is a trial, which is comparable to any object, and in particular to another trial. However, using the signature preceding, we find that the call is rejected. The inferred type must be trial, but trial does not implement Comparable<trial>. It isnt necessary that T be comparable to exactly itself. All thats required is that T be comparable to one of its super types. This gives us the actual signature of Collections.max() which is more involved:
public static <T extends Comparable<? super T>> T max(Collection<T> coll)

This reasoning applies to almost any usage of Comparable that is intended to work for arbitrary types: You always want to use Comparable<? super T>. In general, if you have an API that only uses a type parameter T as an argument, its uses should take advantage of lower bounded wildcards (? super T). Conversely, if the API only returns T, youll give your clients more flexibility by using upper bounded wildcards (? extends T).

Now in the further topics, well show you how to work with Generic types in Java.

Implementing Generic Class Type


OK, the Novice Programmer says, I want to learn what generics is all about. Can you explain it? Yes, we can understand the Generics; lets initiate it with an example you say. Heres an example showing the generic type. After JDK 1.5, Java permits you to declare classes, interfaces, and methods with generic types. The java.lang.Comparable interface is declared as shown here which is prior to JDK 1.5, and subsequently the modified version is also there which is as per JDK 1.6:
package java.lang; public interface Comparable { public int compareTo(Object o) }

Generic Types

Declaration as per JDK 1.5 and later:


package java.lang; public interface Comaprable<T> { public int compareTo(T o) }

New Declaration
A definition of generic type looks very much like the definition of an ordinary class, but with a parameter specification with the class name. The parameter that appears between the angled bracket<>, follows the generic type name, is called a type parameter. Here, <T> represents a formal generic type, which can be replaced with an actual concrete type later. The name, T, identifies the type parameter, and we use the parameter name in the definition of the methods and fields in the generic type where there is a dependency on the arguments value for this parameter in the implementation detail. Occurrences of the type parameter name in the definition of a generic type are called type variables because they will be replaced by a value that is a type, in a similar way to how method parameters are replaced by the arguments that you supply. To create a class from the generic type, you just supply an appropriate argument for the parameter between the angled brackets. All the occurrences of the type, T that appears in the definition will be replaced by the type argument that you supply. This will result in a class type that you can use to create an object that implements a linked list that will store objects of the type that you specified as shown in the Figure A. 1. Thus, a generic type essentially defines a set of typesthe set being produced by different arguments for the parameters or parameters for the generic type. This is to note at this stage that the Figure A.S.1 shows three types being defined, there arent three classes. There are just the generic class types to which you supply a type argument to produce a particular type. The three types in Figure A. 1 are produced by plugging the three type arguments shown into the generic type. You can supply only a class or interface type such as type String or type Point as an argument for a type parameter in a generic type. In other words, you cannot use a primitive type such as int or type double as an argument, although, of course, you can use type Integer or type Double. When you create a particular type from the generic type definition by supplying an argument value for T, the argument will be substituted for every occurrence of T in the generic type specification. This applies to field as well as the definitions of methods in the generic type.

Figure A.1: Showing three classes.

Appendix A

For example, the following statement declares that s is a reference variable whose type is Comparable<String>:
Comparable<String> s = "Hello All! Welcome to Java 1.5";

So, you can assign a string to s since String implements Comparable, but you cannot assign an integer object to s (such as s = new Integer (1)), even though Integer implements Comparable. The StackOfIntegers Class (Optional) is used to generalize the element type with a generic type. The new stack class named GenericStack is given here:
public class GenericStack<E> { private E[] elements; private int size; public app() { this(16); } public app(int capacity) { elements = (E[])new Object[capacity]; } public E push(E value) { if (size >= elements.length) { E[] tmp = (E[])new Object[elements.length * 2]; System.arraycopy(elements, 0, tmp, 0, elements.length); elements = tmp; } return elements[size++] = value; } public E pop() { return elements[--size]; } public E peek() { return elements[size - 1]; } public boolean empty() { return size == 0; } public int getSize() { return size; } }

The class declaration has the formal parameter defined <E>. The class uses the elements as object to store the items in the stack. The push method accepts a parameter of type E. The pop method returns the value of type E. If the array is empty then the pop method returns null. The peek method also returns the value of type E. The getSize method returns the arrays length. Heres an example that gives rise to a stack to hold strings and adds three strings to the stack:
app<String> stack = new app<String>(); stack.push("Washington DC"); stack.push("Las Vegas"); stack.push("New York");

Heres another example that generates a stack to hold integers and adds three integers to the stack:
app<Integer> stack = new app<Integer>(); stack.push(1); stack.push(2); stack.push(3);

Interoperability
The Novice Programmer says Here we are assuming that everyone is using the latest version of the Java programming language, which supports generics, unfortunately, this isnt the case. Millions of lines of

Generic Types

code have been written in earlier versions of the language, and these all cannot be converted overnight. So in this situation what can be done? You say, Well tackle this situation by converting the old code to the generics. In this part, well focus on a simpler problem how can legacy code and generic code interoperate? This has two parts: using legacy code in generic code, and using generic code within legacy code.

Using Legacy Code in Generics


How can you use old code, while you are still enjoying the benefits of generics in your own code? For example, assume that you want to use the package genericcode. The folks at abc.com market a system for inventory control, highlights of which are shown here:
package genericcode; public interface Part { . . . } public class Inventory { public static void addAssembly(String name, Collection parts) { . . . } public static Assembly getAssembly(String name) { . . . } } public interface Assembly { Collection getParts(); }

Now, youd like to add new code that uses the preceding-mentioned API. It would be nice to ensure that you always called addAssembly() with the proper arguments (abc.com is a purely fictional company, used for illustration purposes. Any relation to any real company or institution, or any persons living or dead, is purely coincidental). The collection you pass in is indeed a Collection of Part. Of course, generics are tailor-made for this:
package inventory; import genericcode.*; public class Blade implements Part { . . . } public class Razor implements Part { } public class Main { public static void main(String[] args) { Collection<Part> col = new ArrayList<Part>(); col.add(new Razor()) ; col.add(new Blade()); Inventory.addAssembly(thingee, col); Collection<Part> kol = Inventory.getAssembly(thingee).getParts();} }

When we call addAssembly, the second parameter should be of type Collection. The actual argument is of type Collection<Part>. This works, but why? After all, most of the collections dont contain Part objects, and so in general, the compiler never knows what kind of collection the type Collection refers to. In proper generic code, Collection would always be accompanied by a type parameter. When it does not happen, its called a raw type. The first perception of man people is that Collection really means Collection<Object>. However, as explained earlier, it isnt safe to pass a Collection<Part> in a place where a Collection<Object> is required. In reality, the type Collection denotes a collection of some unknown type, just like Collection<?>. But wait, that cant be right either!

Appendix A

Consider the call to getParts(), which returns a Collection. This is then assigned to kol, which is a Collection<Part>. If the result of the call is a Collection<?>, the assignment would be an error. In reality, the assignment is legal, but it generates an unchecked warning. The warning is needed, because the compiler cant guarantee its correctness. In getAssembly(), we have no way of checking the legacy code to ensure that the collection being returned is a collection of Parts. The type used in the code is Collection, and in such a collection, one can legally insert all kinds of objects. So, shouldnt this be an error? Theoretically, yes; but practically, if generic code is going to call legacy code, this has to be allowed. Its up to the programmer, to satisfy yourself that in this case, the assignment is safe because the contract of getAssembly() says it returns a collection of Parts, even though the type signature doesnt show this. So, raw types are very much like wildcard types, though not type-checked as stringently. To allow generics to interoperate with pre-existing legacy code, it is a deliberate design decision. It is inherently dangerous to call legacy code from generic code; once you mix generic code with non-generic legacy code. All the safety guarantees that the generic type system usually provides are void. But, you are still better off than you were without using generics at all because at least you know that the code at your end is consistent. At this moment of time, there are a lot more non-generic codes than the generic codes, and there will inevitably be situations where they have to mix. You have to pay close attention to the unchecked warnings if you find that you must intermix legacy and generic code. Think carefully how you can justify the safety of the code that creates the warning.

Using Generic Code within Legacy Code


Now lets reverse our case. Suppose that abc.com chose to convert their API to use generics, but that some of their clients are not interested in doing so. So now the code of supplier looks like:
package genericcode; public interface Part { . . . } public class Inventory { public static void addAssembly(String name, Collection<Part> parts) {...} public static Assembly getAssembly(String name) {...} } public interface Assembly { Collection<Part> getParts(); }

And the code of client appears like:


package inventory; import genericcode.*; public class Blade implements Part { ... } public class Razor implements Part { } public class Main { public static void main(String[] args) { Collection col = new ArrayList(); col.add(new Razor()) ; col.add(new Blade()); Inventory.addAssembly(thingee, c); Collection kol = Inventory.getAssembly(thingee).getParts(); } }

Generic Types

Though the client code was written before introduction of generics, but it uses the package genericcode and the collection library, both of which are using generic types. So we can say that all the uses of generic type declarations in the client code are raw types. Line 1 gives an unchecked warning, because a raw Collection is being passed in where a Collection of Parts is expected, and the compiler cannot guarantee that the raw Collection really is a Collection of Parts. Alternatively, you can compile the client code using the source 1.4 flag, ensuring that no warnings are generated. However, in that case, you wont be able to use any of the new language features introduced in JDK 1.5.

Erasure and Translation


Novice Programmer asks, Why was generics implemented in Java? You say, Need for compatibility with previous version of Java was the need of the hour. Simply put, generic code had to be compatible with pre-existing, non-generic code. The way Java implements generics while satisfying this constraint is through the use of erasure. Even though it is not necessary to know how the Java compiler transforms the source code into object code, in the case of generics, some general understanding is necessary. Now take a look at this code:
public String holes(Integer i) { List<String> lst = new LinkedList<String>(); List xst = lst; xst.add(i); return lst.iterator().next(); }

Here, we have aliased a list of strings and a plain old list. We insert an Integer into the list, and attempt to extract a String. This seems to be wrong. If we try to execute this code ignoring the warning, it will fail exactly at the point where we try to use the wrong type. At runtime, this code behaves like:
public String holes(Integer i) { List lst = new LinkedList; List xst = lst; xst.add(i); return (String) lst.iterator().next(); }

When an element is extracted from the list, and an attempt is made to treat it as a string by casting it to String, well get a ClassCastException. Same thing happens with the generic version of holes(). The reason behind this is that generics are implemented by the Java compiler as a front-end conversion called erasure. You can (almost) think of it as a source-to-source translation, whereby the generic version of holes() is converted to the non-generic version. As a result, even in the presence of unchecked warnings, the type safety and integrity of the Java virtual machine are never at risk. Basically, erasure gets rid of (or erases) all generic type information. All the type information between angle brackets is thrown out, for example, a parameterized type like List<String> is converted into List. All remaining uses of type variables are replaced by the upper

Appendix A

bound of the type variable (usually Object). And, whenever the resulting code isnt type-correct, a cast to the appropriate type is inserted, as in the last line of holes. Its good to know a little bit about erasure, especially when you want to do more complicated things, for example, converting existing APIs to use generics, or just want to understand why things are the way they are.

Wildcard Parameterized Type


You express a particular type from the set defined by a generic type by supplying a type argument for each of its type parameters. For example, take the problem of writing a routine, which prints out all the elements in a collection. Heres how you might write it in an older version of the language:
void printCollection(Collection col) { Iterator x = col.iterator(); for (k = 0; k < c.size(); k++) { System.out.println(x.next()); } }

And heres a naive attempt at writing it using generics (and the new for loop syntax):
void printCollection(Collection<Object> col) { for (Object x : col) { System.out.println(x); } }

The problem is that this new version is much less useful than the old one. Whereas the old code could be called with any kind of collection as a parameter, the new code only takes Collection<Object>, which, as we have just demonstrated, is not a super type of all kinds of collections! So what is the super type of all kinds of collections? Its written Collection<?> (pronounced collection of unknown), that is, a collection whose element type matches anything. Its called a wildcard type for obvious reasons. We can write:
void printCollection(Collection<?> col) { for (Object x : col) { System.out.println(x); } }

Now, we can call it with any type of collection. Notice that inside printCollection(), we can still read elements from c and give them type Object. This is always safe as it does contain objects whatever is the actual type of the collection. However, it isnt safe to add arbitrary objects to it:
Collection<?> col = new ArrayList<String>(); col.add(new Object());

We cant add objects to it, since we dont know for what the element type of col stands. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we want to add should be a subtype of this unknown type. Since we dont know the type of that, we cant pass anything in. The sole exception is null, which is a member of every type. On the other hand, given a List<?>, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object.

Generic Types

Therefore, it is safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.

Arrays of Variables of a Parameterized Type


The component type of an array object may not be a type variable or a parameterized type, unless it is a (unbounded) wildcard type. You can declare array types whose element type is a type variable or a parameterized type, but not array objects. This is annoying, to be sure. This restriction is necessary to avoid situations like:
List<String>[] lsa = new List<String>[10]; Object o = lsa; Object[] ob = (Object[]) o; List<Integer> lst = new ArrayList<Integer>(); lst.add(new Integer(3)); ob[1] = lst; String s = lsa[1].get(0);

If arrays of parameterized type were allowed, the preceding example would compile without any unchecked warnings, and yet fail at runtime. We have had type-safety as a primary design goal of generics. In particular, the language is designed to guarantee that if your entire application has been compiled without unchecked warnings using javac -source 1.5, it is type safe. However, you can still use wildcard arrays. Here are two variations on the previous code. The first forgoes the use of both array objects and array types whose element type is parameterized. As a result, we have to cast explicitly to get a String out of the array:
list<?>[] lsa = new List<?>[10]; Object o = lsa; Object[] ob = (Object[]) o; List<Integer> lst = new ArrayList<Integer>(); lst.add(new Integer(3)); ob[1] = lst; String s = (String) lsa[1].get(0);

In the next variation, we refrain from creating an array object whose element type is parameterized, but still use an array type with a parameterized element type. This is legal, but generates an unchecked warning. Indeed, the code is unsafe, and eventually an error occurs:
List<String>[] lsa = new List<?>[10]; Object o = lsa; Object[] ob = (Object[]) o; List<Integer> lst = new ArrayList<Integer>(); lst.add(new Integer(3)); oa[1] = lst; String s = lsa[1].get(0);

Similarly, attempting to create an array object whose element type is a type variable causes a compiletime error:

Appendix A

<T> T[] makeArray(T t) { return new T[100]; }

Since type variables dont exist at runtime, there is no way to determine what the actual array type would be. The way to work around these kinds of limitations is to use class literals as runtime type tokens. As of the time of this writing, arrays of generic types and arrays of type variables are not allowed. Attempting to create an array of parameterized Vectors, for example, causes a compiler error:
import java.util.*; public class GenericArrayExample { public static void main(String args[]) { Vector<Integer> vectorList[] = new Vector<Integer>[10]; } }

If you try to compile that code, the compiler issues the following two errors. This code is the simplest approach to creating an array of a generic type and the compiler tells you explicitly that creating a generic type array is forbidden:
GenericArrayExample.java:6: generic array creation Vector<Integer> vectorList[] = new Vector<Integer>[10]; ^ 1 error

Generic Constructors
A constructors is a specialized kind of method and you can define class constructors with their own independent parameters. You can define parameterized constructors for both ordinary classes and generic class type. Lets take an example. Let say, you want to add a constructor to the BinaryTree<> type definition that will accept an argument that is an array of items to be added to the binary tree. In this case, defining the constructor as a parameterized method gives you the same flexibility you have with the add() method. Heres how the constructor definition looks:
public<E extends T> BinaryTree(E[] treeitems) { for(E treeitem : treeitems) { add(treeitem); } }

The constructor parameter is E. You have defined this with an upper bound of T, so that argument to the constructor can be an array of elements of the type specified by the type variable T or any subclass of T. For example, if you define a binary tree of type BinaryTree<Person>, then you can pass an array to the constructor with elements of type Person or any type that is a subclass of Person. Lets understand this with the help of an example. The definition of BinaryTree<> will be as follows:
public class app,T extends Comparable<T>> { no-arg constructor public app() { }

Generic Types parameterized constructor public <E extendsT> BinaryTree(E{ } treeitems) { for(E treeitem : treeitems) { add(treeitem); } } //Add a value to the tree public <E extends T> void add (E val) { if(root == null { root = new Node (val); } else { } } //Recursive insertion of an object private <E extends T> void add(E val, Node n) { int comparison = n.obj.compareTo(val); if(comparison == 0) { ++n.count; return; } if(comparison > 0) { if(n.left ==null) { n.left = new Node(val); } else { add(val, n.left); } } else { if (n.right == null) { n.right = new Node(val); } else { add (val, n.right); } } }

Create a list containing the values from the tree in sequence:


Public linkedList<T> sort() { treeSort(root); return val; }

The only change from the previous version are the addition of the constructor that accepts an array as an argument and the definition of the no-org constructor, which is not supplied by the compiler when you explicitly define a constructor of your own. Because the constructor has an independent parameter and that parameter has the type variable for the BinaryTree<> type as its upper bound, the constructor accepts the managers array as the argument because it is a subclass of Person, the type argument that you use to specify the type of the binary tree object. The output shows that the array elements were added to the binary tree and were successfully extracted and stored in sequence in a linked list by the sort() method. Consider this small program which shows the possibility for constructors to be generic:
import java.util.*; class constr { private double val; <T extends Number> constr(T arg) { val = arg.doubleValue(); } void showval() { System.out.println("Constructor value: " + val); } } class constrDemo { public static void main(String args[]) { constr test = new constr(90);

Appendix A constr test2 = new constr(33.5F); test.showval(); test2.showval(); } }

Heres the output:


Constructor value: 90.0 Constructor value: 33.5

Generic Methods
The Novice Programmer says, Can we declare generic interfaces and classes and use generic types to declare generic methods. You say, yes we can explain it by taking an examplea generic method print (Lines 1014) to print an array of objects. Line 6 passes an array of integer objects to invoke the generic print method whereas Line 7 invokes print with an array of strings (you can find this code file, gen_met.java on CD):
public class gen_met { public static void main(String[] args ) { Integer[] integers = {1, 2, 3, 4, 5}; String[] strings = {"Melbourne", "Moscow", "Rome", "Cairo"}; print(integers); print(strings); } public static <E> void print(E[] list) { for (int i = 0; i < list.length; i++) System.out.print(list[i] + " "); System.out.println(); } }

Heres the result:


C:\java gen_met 1 2 3 4 5 Melbourne Moscow Rome Cairo

Consider writing a method that takes an array of objects and a collection and puts all objects in the array into the collection. Here is a first attempt:
static void fromArrayToCollection(Object[] ob, Collection<?> col) { for (Object ob : a) { col.add(ob); } }

By now, youll have learned to avoid the beginners mistake of trying to use Collection<Object>, as the type of the collection parameter. You may or may not have recognized that using Collection<?> isnt going to work either. Recall that you cannot just shove objects into a collection of unknown type. The way to do deal with these problems is to use generic methods. Just like type declarations, method declarations can be generic that is, parameterized by one or more type parameters:

Generic Types

static <T> void fromArrayToCollection(T[] atcol, Collection<T> col) { for (T o : atcol) { col.add(o); } }

We can call this method with any kind of collection whose element type is a super type of the element type of the array:
Object[] ob = new Object[100]; Collection<Object> col = new ArrayList<Object>(); fromArrayToCollection(ob, col); String[] sst = new String[100]; Collection<String> colst = new ArrayList<String>(); fromArrayToCollection(sst, colst); fromArrayToCollection(sst, col); Integer[] i = new Integer[100]; Float[] f = new Float[100]; Number[] n = new Number[100]; Collection<Number> coln = new ArrayList<Number>(); fromArrayToCollection(i, coln); fromArrayToCollection(f, coln); fromArrayToCollection(n, coln); fromArrayToCollection(n, col); fromArrayToCollection(n, colst);

Notice that we dont have to pass an actual type argument to a generic method. The compiler infers the type argument for us, based on the types of the actual arguments. It will generally infer the most specific type argument that will make the call type-correct. One question that arises is: when should we use generic methods, and when should we use wildcard types? To understand the answer, lets examine a few methods from the Collection libraries:
interface Collection<E> { public boolean containsAll(Collection<?> col); public boolean addAll(Collection<? extends E> c); }

We could have used generic methods here instead:


interface Collection<E> { public <T> boolean containsAll(Collection<T> col); public <T extends E> boolean addAll(Collection<T> c); }

However, in both containsAll and addAll, the type parameter T is used only once. The return type doesnt depend on the type parameter, nor does any other argument to the method (in this case, there simply is only one argument). This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites. If that is the case, one should use wildcards. Wildcards are designed to support flexible subtyping, which is what we are trying to express here. Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there

Appendix A

isnt such a dependency, a generic method should not be used. It is possible to use both generic methods and wildcards in tandem. Here is the method:
Collections.copy(): class Collections { public static <T> void copy(List<T> dst, List<? extends T> src){...} }

Note the dependency between the types of the two parameters. Any object copied from the source list, src, must be assignable to the element type T of the destination list, dst. So the element type of src can be any subtype of Twe dont care which. The signature of copy expresses the dependency using a type parameter, but uses a wildcard for the element type of the second parameter. We could have written the signature for this method another way, without using wildcards at all:
class Collections { public static <T, S extends T> void copy(List<T> dest, List<S> src){...} }

This is fine, but while the first type parameter is used both in the type of dst and in the bound of the second type parameter, S, S itself is only used once, in the type of srcnothing else depends on it. This is a sign that we can replace S with a wildcard. Using wildcards is clearer and more concise than declaring explicit type parameters, and should therefore be preferred whenever possible. Wildcards also have the advantage that they can be used outside of method signatures, as the types of fields, local variables and arrays. Here is an example. Returning to our shape drawing problem, suppose we want to keep a history of drawing requests; we can maintain the history in a static variable inside class Shape, and have drawAll() store its incoming argument into the history field:
static List<List<? extends Shape>> history = new ArrayList<List<? extends Shape>>(); public void drawAll(List<? extends Shape> shapes) { history.addLast(shapes); for (Shape s: shapes) { s.draw(this); } }

Finally, again lets take note of the naming convention used for the type parameters. We use T for type, whenever there isnt anything more specific about the type to distinguish it. This is often the case in generic methods. If there are multiple type parameters, we might use letters that neighbor T in the alphabet, such as S. If a generic method appears inside a generic class, its a good idea to avoid using the same names for the type parameters of the method and class, to avoid confusion. The same applies to nested generic classes. In addition to the generic mechanism for classes, generic methods are introduced. The angle brackets for the parameters appear after all method modifiers but before the return type of the method. Following is an example of a declaration of a generic method:
static <Elem> void swap(Elem[] a, int i, int j) { Elem temp = a[i];

Generic Types a[i] = a[j]; a[j] = temp; }

The syntax for the parameters in a generic method is the same as that for generic classes. Type variables can have bounds just like they do in class declarations. Two methods cannot have the same name and argument types. If two methods have the same name and argument types, and have the same number of type variables with the same bounds, then these methods are the same and the compiler will generate an error.

Parameterized Types and Inheritance


The Novice Programmer says Ok, now tell me can we define a class as a subclass of a class type that is an instance of a generic type. You say, Of course we can, lets go through the following example to understand this. You could derive a new class from type LinkedList<Person> or from type BinaryTree<String>. Methods and fields will be inherited from the base class in the usual way. However, you can encounter complications because of the way the compiler translates methods that involve type arguments into bytecodes, so lets first understand that process. The method that involves parameter and/or the return value type specified by a type argument is translated by the compiler to a method with the same name, but with the type of each parameter whose type is a type variable replaced by its left most bound. Casts are inserted in the body of the translated method wherever necessary to produce the actual type required. You will find this in an example that clarifies this. We had already seen the version of the Linkedlist<> type defined as:
public LinkedList<T extends Object & Serializable> { public void addItem(T listitem) { } }

If you define an object of type Linkedlist <String>, notionally the addItem() method for the object is like this:
public void addItem(String itemstring) { }

However, the compiler will translate the addItem() method to the following:
public void addItem(Object itemobject) { }

Normally you dont need to be aware of this. However, suppose you derive a new class from type Linkedlist<String>:
public class SpecialList extends LinkedList<String> { public void addItem(String itemstring) {} }

Appendix A

SpecialList class inherited from Linkedlist<String> has a method additem(), which will overwrite the additem() method available in Linkedlist class. Because of the difference in parameter type of both the methods, compiler will not be able to overwrite the additem() method of base class, i.e. Linkedlist class. Parameter of the additem() method of the Linkedlist class is of object type whereas parameter of the additem() method of the SpecialList class is of String type. To fix the problem the compiler will create a bridge method in your derived special class that will look like:
public void addItem(Object itemobject) { addItem((String)itemobject); }

The effect of the bridge method is to convert any calls to the inherited version of addItem() to a call to your version, thus making your overwrite of the base class method effective. However, the approach addopted by the compiler has implications for you. You must take care not to define methods in your derived class that have the same signature as an inherited method. Since the compiler changes the parameter types and return types involving type variables to their bounce, you must consider the inherited methods in these terms when you are defining your derived class methods. If a method in a class that you have derived from the generic type has the same signature as the erasure of an inherited method, you code will not compile.

Benefits of Using Generic Types


Instead of using a generic type, you could simply make the type element Object that may accommodate any object type. However generics reduce the need to cast references back to the actual types of the object, and improve the software readability and robustness. The frequency of ClassCastExceptions will be reduced because the compiler will catch type errors at compile time rather than at runtime. Errors caught by the compiler are a good thing; runtime exceptions are not as good. Additionally the need to surround casts with if (R instanceof) is also gone. The addition of parameterized types allows you to create code that is geared to collections of objects of a single specific type. The following example doesnt need to check type or cast, and the code is cleaner because of this. This static method filters all values larger than a given cap out of any given collection of Integers:
public static void capiLst (Collection list, int cap) { Iterator rep = list.iterator(); while (rep.hasNext()) { if(rep.next().intValue() > cap) { rep.remove(); } } }

Notice theres no need for the author of this method to cast Object to Integer; the compiler will take care of it. In case the user attempts to pass the wrong type of object, it will be refused by the compiler, giving a cannot be applied compiler error. When the same code is rewritten without a generic collection, it would require you to cast each element retrieved from the collection. This is not a problem, but the generic example is cleaner as compared to the one without.

Generic Types

Suppose the same method is included here but without the addition of generics. Notice the cast in the if-statement and the use of instanceof to check the type before using it as an Integer:
public static void capiLst (Collection<Integer> list, int cap) { Iterator<Integer> rep = list.iterator(); Object R = null; while (rep.hasNext()) { R = rep.next(); if ( R instanceof Integer && ((Integer)R).intValue() > cap) { rep.remove(); } } }

Code developers will use generics to make code more flexible in more than just collections.

Generic Type Limitation


There are few limitations that you need to keep in mind when using generics. They involve creating objects of a type parameter exceptions arrays and static members. We can examine these one by one.

Type Parameters cant be Instantiated


You cannot create an instance by using a generic type parameter. For example consider this class:
class Genric<T> { T obj; Genric() { obj = new T(); } }

Here we cannot create an instance of T. The reason should be easy to understand because T does not exist at runtime. It means the compiler is unable to understand, what type of object to create. Here, the one point to be understood is that erasure removes all type parameters during the compiler process.

Restrictions on Static Members


No static member can use a type parameter declared by the enclosing class. For example, all of the static members of this class are illegal:
class Genillegal<T> { static T obj; static T getobj() { return obj; } static void displayobj() { System.out.println(obj); } }

Although here we are not declaring static members that use a type parameter declared by the enclosing class but we can declare static generic method, which define their own type parameters.

Appendix A

Restrictions on Generic Arrays


In arrays, there are two important generic restrictions. First you cannot instantiate an array whose base type is a type parameter. Second, you cannot create an array of type specific generic references. The program here shows both the situations:
class Genric<T extends Number> { T obj; T values[]; Gen(T obt, T[] nums) { obj = obt; values = nums; } } class GenArr { public static void main(String args[]) { Integer num[] = { 1, 2, 3, 4, 5}; Genric<Integer> iobj = new Genric<Integer>(50, num); Genric<?> gns[] = new Genric<?>[10]; } }

As the program shows, it is valid to declare a reference to an array of type, as this line does:
T values[];

But, you cannot instantiates an array of T, as this commented-outline attempt:


// values = new T[10];

The reason you cannot create an array of T is that T does not exist at runtime, so there is no way for the compiler to know what type array to actually create.

Restrictions of Generic Exceptions


A generic class cannot extend Throwable. This means that you cannot create generic exception classes. So, similar to generic classes, generic interfaces and generic methods, the creation of a generic exception class is not possible. This appendix updates reader with the new extension to the Java programming language i.e. introduction of generics. A generic type is known as parameterized type. This helps in creating general classes, interfaces and methods which can be used for any data type. The type on which the generic classes, interfaces and methods work, is passed as parameter. The use of generics improves readability and robustness of the code. It reduces the chances of ClassCastExceptions as all type errors are caught at compile time. With all benefits, the generic comes with some limitations too, which also have been discussed in the appendix.

Anda mungkin juga menyukai