Generic
Class Hierarchies
Generic classes can be part of a class
hierarchy in just the same way as a non-generic class. Thus, a generic class
can act as a superclass or be a subclass. The key difference between generic
and non-generic hierarchies is that in a generic hierarchy, any type arguments
needed by a generic superclass must be passed up the hierarchy by all
subclasses. This is similar to the way that constructor arguments must be
passed up a hierarchy.
Using
a Generic Superclass
Here is a simple example of a
hierarchy that uses a generic superclass:
// A simple generic class hierarchy.
class Gen<T> {
T ob;
Gen(T o) { ob = o;
}
// Return ob.
T getob() { return ob;
}
}
// A subclass of Gen.
class Gen2<T> extends Gen<T> {
Gen2(T o) {
super(o);
}
}
In this hierarchy, Gen2 extends the generic class Gen. Notice how Gen2 is declared by the following line:
class Gen2<T> extends Gen<T> {
The type parameter T is specified by Gen2 and is also passed to Gen
in the extends clause. This means
that whatever type is passed to Gen2
will also be passed to Gen. For
example, this declaration,
Gen2<Integer> num = new
Gen2<Integer>(100);
passes Integer as the type parameter to Gen. Thus, the ob inside
the Gen portion of Gen2 will be of type Integer.
Notice also that Gen2 does not use the type parameter T except to support the Gen superclass. Thus, even if a
subclass of a generic superclass would otherwise not need to be generic, it
still must specify the type parameter(s) required by its generic superclass.
Of course, a subclass is free
to add its own type parameters, if needed. For example, here is a variation on
the preceding hierarchy in which Gen2
adds a type parameter of its own:
// A subclass can add its own type parameters.
class Gen<T> {
T ob; // declare an object of type T
//Pass the constructor a reference to an object
of type T.
Gen(T o) { ob = o;
}
// Return ob.
T getob() { return ob;
}
}
//A subclass of Gen that defines a second type
parameter, called V.
class Gen2<T, V> extends Gen<T> { V
ob2;
Gen2(T o, V o2) { super(o);
ob2 = o2;
}
V getob2() { return ob2;
}
}
// Create an object of type Gen2.
class HierDemo {
public static void main(String args[]) {
// Create a Gen2 object for String and Integer.
Gen2<String, Integer> x =
new Gen2<String, Integer>("Value is:
", 99);
System.out.print(x.getob());
System.out.println(x.getob2());
}
}
Notice the declaration of
this version of Gen2, which is shown
here:
class Gen2<T, V> extends Gen<T> {
Here, T
is the type passed to Gen, and V is the type that is specific to Gen2. V is used to declare an object called ob2, and as a return type for the method getob2( ). In main( ), a
Gen2 object is created in which type
parameter T is String, and type parameter
V is Integer. The program displays the following,
expected, result:
Value is: 99
A
Generic Subclass
It is perfectly acceptable
for a non-generic class to be the superclass of a generic subclass. For
example, consider this program:
//A non-generic class can be the superclass of
a generic subclass.
//A non-generic class.
class NonGen { int num;
NonGen(int i) { num = i;
}
int getnum() { return num;
}
}
// A generic subclass.
class Gen<T> extends NonGen {
T ob; // declare an object of type T
//Pass the constructor a reference to an object
of type T.
Gen(T o, int i) { super(i);
ob = o;
}
// Return ob.
T getob() {
return ob;
}
}
// Create a Gen object.
class HierDemo2 {
public static void
main(String args[]) {
// Create a Gen
object for String.
Gen<String> w = new Gen<String>("Hello", 47);
System.out.print(w.getob() + " ");
System.out.println(w.getnum());
}
}
The output from the program
is shown here:
Hello 47
In the program, notice how Gen inherits NonGen in the following declaration:
class Gen<T> extends NonGen {
Because NonGen is not generic, no type argument is specified. Thus, even
though Gen declares the type
parameter T, it is not needed by
(nor can it be used by) NonGen.
Thus, NonGen is inherited by Gen in the normal way. No special
conditions apply.
Run-Time
Type Comparisons Within a Generic Hierarchy
Recall the run-time type
information operator instanceof that
was described in Chapter 13. As explained, instanceof
determines if an object is an instance of a class. It returns true if an object
is of the specified type or can be cast to the specified type. The instanceof operator can be applied to
objects of generic classes. The following class demonstrates some of the type
compatibility implications of a generic hierarchy:
// Use the instanceof operator with a generic
class hierarchy.
class Gen<T> {
T ob;
Gen(T o) { ob = o;
}
// Return ob.
T getob() { return ob;
}
}
// A subclass of Gen.
class Gen2<T> extends Gen<T> {
Gen2(T o) {
super(o);
}
}
//Demonstrate run-time type ID implications of
generic class hierarchy.
class HierDemo3 {
public static void main(String args[]) {
//Create a Gen object for Integers.
Gen<Integer> iOb = new
Gen<Integer>(88);
//Create a Gen2 object for Integers.
Gen2<Integer> iOb2 = new
Gen2<Integer>(99);
//Create a Gen2 object for Strings.
Gen2<String> strOb2 = new Gen2<String>("Generics
Test");
//See if iOb2 is some form of Gen2.
if(iOb2 instanceof Gen2<?>)
System.out.println("iOb2 is instance of
Gen2");
//See if iOb2 is some form of Gen.
if(iOb2 instanceof Gen<?>)
System.out.println("iOb2 is instance of
Gen");
System.out.println();
See if strOb2 is a Gen2. if(strOb2 instanceof
Gen2<?>)
System.out.println("strOb2 is instance of
Gen2");
See if strOb2 is a Gen.
if(strOb2 instanceof Gen<?>)
System.out.println("strOb2 is instance of
Gen");
System.out.println();
//See if iOb is an instance of Gen2, which it
is not.
if(iOb instanceof Gen2<?>)
System.out.println("iOb is instance of
Gen2");
//See if iOb is an instance of Gen, which it
is.
if(iOb instanceof Gen<?>)
System.out.println("iOb is instance of
Gen");
//The following can't be compiled because generic
type info does not exist at run time.
if(iOb2 instanceof Gen2<Integer>)
System.out.println("iOb2 is instance of
Gen2<Integer>");
}
}
The output from the program
is shown here:
iOb2 is instance of Gen2
iOb2 is instance of Gen
strOb2 is instance of Gen2
strOb2 is instance of Gen
iOb is instance of Gen
In this program, Gen2 is a subclass of Gen,
which is generic on type parameter T. In main( ), three objects are created.
The first i s iOb, which is an
object of type Gen<Integer>. The second is iOb2, which is an instance of Gen2<Integer>.
Finally, strOb2 is an object of type
Gen2<String>.
Then, the program performs
these instanceof tests on the type
of iOb2:
See if iOb2 is some form of Gen2. if(iOb2
instanceof Gen2<?>)
System.out.println("iOb2 is instance of
Gen2");
//See if iOb2 is some form of Gen.
if(iOb2 instanceof Gen<?>)
System.out.println("iOb2 is instance of
Gen");
As the output shows, both
succeed. In the first test, iOb2 is
checked against Gen2<?>. This
test succeeds because it simply confirms that iOb2 is an object of some type of Gen2 object. The use of the wildcard enables instanceof to determine if iOb2
is an object of any type of Gen2.
Next, iOb2 is tested against Gen<?>, the superclass type.
This is also true because iOb2 is
some form of Gen, the superclass.
The next few lines in main( ) show
the same sequence (and same results)
for strOb2.
Next, iOb, which is an instance of Gen<Integer>
(the superclass), is tested by these lines:
See if iOb is an instance of Gen2, which it is
not.
if(iOb instanceof Gen2<?>)
System.out.println("iOb is instance of
Gen2");
//See if iOb is an instance of Gen, which it
is.
if(iOb instanceof Gen<?>)
System.out.println("iOb is instance of
Gen");
The first if fails because iOb is not some type of Gen2
object. The second test succeeds because iOb
is some type of Gen object.
Now, look closely at these
commented-out lines:
//The following can't be compiled because
//generic type info does not exist at run time.
if(iOb2 instanceof Gen2<Integer>)
System.out.println("iOb2 is instance of
Gen2<Integer>");
As the comments indicate,
these lines can’t be compiled because they attempt to compare iOb2 with a specific type of Gen2, in this case, Gen2<Integer>. Remember, there
is no generic type information
available at run time. Therefore, there is no way for instanceof to know if iOb2
is an instance of Gen2<Integer>
or not.
Casting
You can cast one instance of
a generic class into another only if the two are otherwise compatible and their
type arguments are the same. For example, assuming the foregoing program, this
cast is legal:
(Gen<Integer>) iOb2 // legal
because iOb2 includes an instance of Gen<Integer>.
But, this cast:
(Gen<Long>) iOb2 // illegal is not legal because iOb2 is not an instance of Gen<Long>.
Overriding
Methods in a Generic Class
A method in a generic class
can be overridden just like any other method. For example, consider this
program in which the method getob( )
is overridden:
// Overriding a generic method in a generic
class.
class Gen<T> {
T ob; // declare an object of type T
//Pass the constructor a reference to an object
of type T.
Gen(T o) { ob = o;
}
// Return ob. T getob() {
System.out.print("Gen's getob(): " );
return ob;
}
}
// A subclass of Gen that overrides getob().
class Gen2<T> extends Gen<T> {
Gen2(T o) { super(o);
}
// Override getob().
T getob() {
System.out.print("Gen2's getob(): ");
return ob;
}
}
// Demonstrate generic method override.
class OverrideDemo {
public static void main(String args[]) {
//Create a Gen object for Integers.
Gen<Integer> iOb = new
Gen<Integer>(88);
//Create a Gen2 object for Integers.
Gen2<Integer> iOb2 = new
Gen2<Integer>(99);
Create a Gen2 object for Strings.
Gen2<String> strOb2 = new
Gen2<String> ("Generics Test");
System.out.println(iOb.getob());
System.out.println(iOb2.getob());
System.out.println(strOb2.getob());
}
The output is shown here:
Gen's getob(): 88
Gen2's getob(): 99
Gen2's getob(): Generics Test
As the output confirms, the
overridden version of getob( ) is
called for objects of type Gen2, but
the superclass version is called for objects of type Gen.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.