Generics and Relationships in Java - Writing Generic Classes (Page 4 of 5 )
Now that we have (at least some of) the "end user" view of generics, let's try writing a few classes ourselves. In this section, we'll talk about how type variables are used in the definition of generic classes, where they may appear, and some of their limitations. We'll also talk about subclassing generic types.
The Type Variable
We've already seen the basics of how type variables are used in the declaration of a generic class. One or more type variables are declared in the angle bracket (<>) type declaration and used throughout the body and instance methods of the class. For example:
class Mouse { } class Bear { }
class Trap< T > { T trapped;
public void snare( T trapped ) { this.trapped = trapped; } public T release() { return trapped; } } // usage Trap<Mouse> mouseTrap = new Trap<Mouse>(); mouseTrap.snare( new Mouse() ); Mouse mouse = mouseTrap.release();
Here, we created a generic Trap class that can hold any type of object. We used the type variable T to declare an instance variable of the parameter type as well as in the argument type and return type of the two methods.
The scope of the type variable is the instance portion of the class, including methods and any instance initializer blocks. The static portion of the class is not affected by the generic parameterization, and type variables are not visible in static methods or static initializers. As you might guess, just as all instantiations of the generic type have only one actual class (the raw type), they have only one, shared, static context as well. You cannot even invoke a static method through a parameterized type. You must use the raw type or an instance of the object.
The type variable can also be used in the type instantiation of other generic types used by the class. For example, if we wanted our Trap to hold more than one animal, we could create a List for them like so:
List<T> trappedList = new ArrayList<T>();
Just to cover all the bases, we should mention that instantiations of generic types on the type variable act just like any other type and can serve in all the places that other instantiations of a type can. For example, a method in our class can take a List<T> as an argument:
public void trapAll( List<T> list ) { ... }
The effective type of the trapAll() method in a Trap<Mouse> is then simply:
trapAll( List<Mouse> list ) { ... }
We should note that this is not what we mean by the term "generic method." This is just a regular Java method that happens to take a generic type as an argument. We'll talk about real generic methods, which can infer their types from arguments and assignment contexts later in this chapter. A type variable can also be used to parameterize a generic parent class, as well see in the next section.
Subclassing Generics
Generic types can be subclassed just like any other class, by either generic or nongeneric child classes. A nongeneric subclass must extend a particular instantiation of the parent type, filling in the required parameters to make it concrete:
class DateList extends ArrayList<Date> { }
DateList dateList = new DateList(); dateList.add( new Date() ); List<Date> ld = dateList;