Generic Methods
Generic Methods
Generic methods are those methods that are written with a single method declaration
and can be called with arguments of different types. Based on the types of the arguments
passed to the generic method, the compiler handles each method call appropriately. The
compiler will ensure the correctness of whichever type is used. These are some properties of
(rules to define ) generic methods:
All generic method declarations have a type parameter section delimited by angle
brackets (< and >) that precedes the method's return type ( < E > in the next
example).
Each type parameter section contains one or more type parameters separated by
commas. A type parameter, also known as a type variable, is an identifier that
specifies a generic type name.
The type parameters can be used to declare the return type and act as placeholders for
the types of the arguments passed to the generic method, which are known as actual
type arguments.
A generic method's body is declared like that of any other method. Note that type
parameters can represent only reference types, not primitive types (like int, double
and char).
Type parameters can be bounded (bounds are explained later in the article)
EXAMPLE 1:
GMD.<Integer> print(integers);
GMD.<String> print(strings);}
System.out.println(); } }
To declare a generic method, you place the generic type immediately after the
keyword static in the method header. For example, public static void print(E[] list) To invoke
a generic method, prefix the method name with the actual type in angle brackets. For
example, GMD.print(integers); GMD.print(strings); or simply invoke it as follows:
print(integers); print(strings); In the latter case, the actual type is not explicitly specified. The
compiler automatically discovers the actual type.
EXAMPLE 2:
System.out.println(); }
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };, Create arrays Double
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' }; Create arrays Character
class method {
static <T, V extends T> boolean isIn(T x, V[] y) {
for(int i=0; i < y.length; i++)
if(x.equals(y[i]))
return true;
return false;}
public static void main(String args[]) {
Integer nums[] = { 1, 2, 3, 4, 5 };
if(isIn(2, nums))
System.out.println("2 is in nums");
if(!isIn(7, nums))
System.out.println("7 is not in nums");
System.out.println();
Bounded Types:
Consider following Example of Generic Class with Bounded Types:
EXAMPLE 4:
class Stats<T> {
T[] nums; // nums is an array of type T
Stats(T[] o) {// Pass the constructor a reference to an array of type T.
nums = o; }
double average() {// Return type double in all cases.
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue(); // Error!!!
return sum / nums.length; }}
class bounded {
public static void main(String args[]) {
Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Stats<Double> dob = new Stats<Double>(dnums);
w = dob.average();
System.out.println("dob average is " + w);}}
OUTPUT:
error: cannot find symbol
sum += nums[i].doubleValue(); // Error!!!
^
symbol: method doubleValue()
location: class Object
In Stats, the average( ) method attempts to obtain the double version of each number
inthe nums array by calling doubleValue( ). Because all numeric classes, such as Integer and
Double, are subclasses of Number, and Number defines the doubleValue() method, this
method is available to all numeric wrapper classes. The trouble is that the compiler has no
way to know that you are intending to create Stats objects using only numeric types.
Thus,when you try to compile Stats, an error is reported that indicates that the doubleValue( )
method is unknown. To solve this problem, you need some way to tell the compiler that you
intend to pass only numeric types to T. Furthermore, you need some way to ensure that only
numeric types are actually passed.
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 is accomplished through the use of an extends clause when
specifying the type parameter,again consider previous example with modifications:
EXAMPLE 5:
In addition to using a class type as a bound for In-built Class Number, Let’s take
Use Defined Class Example:
class A{
public void printClass(){
System.out.println("I am in super class A");}
}
class B extends A{
public void printClass(){
System.out.println("I am in sub class B"); }
}
class C extends A{
public void printClass(){
System.out.println("I am in sub class C"); }
}
class BoundEx<T extends A>{
private T objRef;
public BoundEx(T obj){
this.objRef = obj; }
The trouble with this attempt is that it will work only with other Stats objects whose
type is the same as the invoking object. For example, if the invoking object is of type
Stats<Integer>, then the parameter ob must also be of type Stats<Integer>. It can’t be used to
compare the average of an object of type Stats<Double> with the average of an object of type
Stats<Short>,for example. Therefore, this approach won’t work except in a very narrow
context and does not yield a general (that is, generic) solution. To create a generic sameAvg(
) method, you must use another feature of Java generics: the wildcard argument. The
wildcard argument is specified by the ?, and it represents an unknown type. Using a wildcard,
here is one way to write the sameAvg( ) method:
// Determine if two averages are the same. Notice the use of the wildcard.
boolean sameAvg(Stats<?> ob) {
if(average() == ob.average())
return true;
return false;
}
Here, Stats<?> matches any Stats object, allowing any two Stats objects to have their
averages compared. The following program demonstrates this:
class Stats<T extends Number> {
T[] nums;
Stats(T[] o) {
nums = o;
}
double average() {// Return type double in all cases.
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue();
return sum / nums.length;
}
// Determine if two averages are the same.Notice the use of the wildcard.
boolean sameAvg(Stats<?> ob) {
if(average() == ob.average())
return true;
return false;
}
}
// Demonstrate wildcard.
class WildcardDemo {
public static void main(String args[]) {
Integer inums[] = { 1, 2, 3, 4, 5 };
Stats<Integer> iob = new Stats<Integer>(inums);
double v = iob.average();
System.out.println("iob average is " + v);
Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Stats<Double> dob = new Stats<Double>(dnums);
double w = dob.average();
System.out.println("dob average is " + w);
Float fnums[] = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F };
Stats<Float> fob = new Stats<Float>(fnums);
double x = fob.average();
System.out.println("fob average is " + x);
// See which arrays have same average.
System.out.print("Averages of iob and dob ");
if(iob.sameAvg(dob))
System.out.println("are the same.");
else
System.out.println("differ.");
System.out.print("Averages of iob and fob ");
if(iob.sameAvg(fob))
System.out.println("are the same.");
else
System.out.println("differ.");}}
OUTPUT:
iob average is 3.0
dob average is 3.3
fob average is 3.0
Averages of iob and dob differ.
Averages of iob and fob are the same.
you can also use an interface type. In fact,you can specify multiple interfaces as
bounds. Furthermore, a bound can include both a class type and one or more interfaces. In
this case, the class type must be specified first.When a bound includes an interface type, only
type arguments that implement that interface are legal. When specifying a bound that has a
class and an interface, or multiple interfaces, use the & operator to connect them. For
example,
class Gen<T extends MyClass & MyInterface> { // ...
interface X{
public void printClass();
}
class Y implements X{
public void printClass(){
System.out.println("I am in class Y"); } }
class Z implements X{
public void printClass(){
System.out.println("I am in class Z"); } }
class BoundExmp<T extends X>{
private T objRef;
public BoundExmp(T obj){
this.objRef = obj; }
public void doRunTest(){
this.objRef.printClass(); }
}
public class MyBoundedInterface {
public static void main(String a[]){
//Creating object of interface X called Y and passing it to BoundExmp as a type
parameter.
BoundExmp<Y> bey = new BoundExmp<Y>(new Y());
bey.doRunTest();
//Creating object of Interface X called Z and passing it to BoundExmp as a type parameter.
BoundExmp<Z> bez = new BoundExmp<Z>(new Z());
bez.doRunTest();
}}
OUTPUT:
I am in class Y
I am in class Z
Generic Constructors
It is also possible for constructors to be generic, even if their class is not. For example,
consider the following short program:
Number, GenCons( ) can be called with any numeric type, including Integer, Float, or
Double. Therefore, even though GenCons is not a generic class, its constructor is generic.
Below program demonstrate the use of User defined class used as TypeBound :
class Area{
public <E extends Number> void printClass(E param){
System.out.println("I am in super class A"); }
}
class Circle extends Area{
public <E extends Number > void printClass(E param){
System.out.println("Area of Circle:"+(3.14*param.doubleValue()*param.doubleValue()));
}
}
class Square extends Area{
public <E extends Number> void printClass( E param){
System.out.println("Area of Square:"+(param.doubleValue()*param.doubleValue())); }
}
class BoundEx<T extends Area,E extends Number>{
T objRef;
E param;
public BoundEx(T obj,E p){
this.objRef = obj;
param = p;
}
public void doRunTest(){
this.objRef.printClass(param); }
}
public class USDclassTypeBound {
public static void main(String args[]){
//Creating object of sub class square and passing it to BoundEx as a type parameter.
BoundEx<Square,Integer> sq = new BoundEx<Square,Integer>(new Square(),5);
sq.doRunTest();
//Creating object of sub class Circle and passing it to BoundEx as a type parameter.
BoundEx<Circle,Double> cr = new BoundEx<Circle,Double>(new Circle(),2.0);
cr.doRunTest();
BoundEx<Area,Float> a = new BoundEx<Area,Float>(new Area(),0.0f);
a.doRunTest();
}}
OUTPUT:
Area of Square:25.0
Area of Circle:12.56
I am in super class A
Generic Interfaces
In addition to generic classes and methods, you can also have generic interfaces.
Generic interfaces are specified just like generic classes. Here is an example. It creates an
interface called MinMax that declares the methods min( ) and max( ), which are expected to
return the minimum and maximum value of some set of objects.
Although most aspects of this program should be easy to understand, a couple of key
points need to be made. First, notice that MinMax is declared like this:
interface MinMax<T extends Comparable<T>> {
In general, a generic interface is declared in the same way as is a generic class. In this case,
the type parameter is T, and its upper bound is Comparable, which is an interface defined by
java.lang. A class that implements Comparable defines objects that can be ordered. Thus,
requiring an upper bound of Comparable ensures that MinMax can be used only with
objects that are capable of being compared. Notice that Comparable is also generic. It take
a type parameter that specifies the type of the objects being compared.
Next, MinMax is implemented by MyClass. Notice the declaration of MyClass,
shown here is wrong:
class MyClass<T extends Comparable<T>> implements MinMax<T> {
Pay special attention to the way that the type parameter T is declared by MyClass and
then passed to MinMax. Because MinMax requires a type that implements Comparable,
the implementing class (MyClass in this case) must specify the same bound. Furthermore,
once this bound has been established, there is no need to specify it again in the implements
clause. In fact, it would be wrong to do so. For example, this line is incorrect and will not
Compile:
Once the type parameter has been established, it is simply passed to the interface
without further modification.In general, if a class implements a generic interface, then that
class must also be generic, at least to the extent that it takes a type parameter that is passed to
the interface. For example, he following attempt to declare MyClass is in error:
Because MyClass does not declare a type parameter, there is no way to pass one to MinMax.
In this case, the identifier T is simply unknown, and the compiler reports an error. Of course,
then the implementing class does not need to be generic.The generic interface offers two
benefits. First, it can be implemented for different types of data. Second, it allows you to put
constraints (that is, bounds) on the types of data for which the interface can be implemented.
In the MinMax example, only types that implement the Comparable interface can be passed
to T. Here is the generalized syntax for a generic interface: