Java Generics - 4. Generic Methods
This is part 4 of the 9 part series on Java Generics
Prev Topic
|
Topics
1. Introduction
2. Bounding
8. Target Types
|
|
Java Generics - Generic Methods
Can you write generic methods on the same lines? Yes you
can. The same rules that applied earlier applies here too. Let us have a look at
the class called GenericMethods.
public class GenericMethods {
public <E> void print(E e){
System.out.println("e=" + e);
}
public static void main(String... args) {
GenericMethods
gm = new GenericMethods();
gm.print("Hello");
gm.print(new Integer(4));
gm.print(new Fruit());
gm.print(new Shiraz());
}
}
|
Focus on the method print(E e). Notice that the method
has a declaration of E within the diamond <E>.
This method accepts any object as shown in the main method. It simply prints
the value. This is an easiest illustration of a typed method. It takes all the
types. It is in fact no different from the method public void print(Object e).
If you try to add another method with this signature, public void print(Object e),
it will give a compiler error - Erasure of method print(Object) is the same
as another method in type GenericMethods. We will talk about the type
erasure in the coming sections. It is sufficient to note at this point that the
two declarations are actually the same.
public class GenericMethods {
public <E> void print(E e){
System.out.println("e=" + e);
}
// compliler error
public void print(Object e){
System.out.println("e=" + e);
}
}
|
The method’s type declaration hides the class’s type
declaration. In the class declaration below, notice that the class GenericMethods<E>
is a typed one with the same symbol E. Even if you initialize the class
with String, the method can still take any typed value.
public class GenericMethods<E> {
public <E> void print(E e){
System.out.println("e=" + e);
}
public static void main(String... args) {
GenericMethods<String>
gm = new
GenericMethods<>();
gm.print("Hello");
gm.print(new Integer(4));
gm.print(new Fruit());
gm.print(new Shiraz());
}
}
|
The <E> declaration in method print(E
e) is actually different and independent of the declaration at the
class GenericMethods<E>. In fact the compiler gives you a
warning at the method declaration - The type parameter E is hiding the type E.
In case you wish to use the type of the class and this was indeed a mistake
then simply remove the declaration of <E> on the method print(E
e) and simply write the signature as public void print(E e) instead of public <E> void print(E e). This
will ensure that the type E is same as the type we used to
initialize the class. If you actually intended it to differ, then you should
use another letter to represent it.
public class GenericMethods<E> {
// Note : No <E> declaration here
public void print(E e){
System.out.println("e=" + e);
}
public static void main(String... args) {
GenericMethods<String>
gm = new GenericMethods<>();
gm.print("Hello");
// compiler error in all the 3 statements. Something like:
// The method print(String) in the type
GenericMethods<String> is
// not applicable for the arguments (Integer)
gm.print(new Integer(4));
gm.print(new Fruit());
gm.print(new Shiraz());
}
}
|
Can you declare more than one type in a method? Why not, go
ahead and let your method take in any number of parameter. Note that too many
parameters in a method make it cumbersome. Look at the declaration below. This
is a valid declaration.
public <P, Q, R> String
concat(P p, Q q, R r){
return "" + p + q + r;
}
|
Note that <P, Q, R> are declared within
the diamond just before the return type. The declaration is very similar to the
one you used when declaring a typed class. You can also bound the declaration for
restricting your methods to take a particular type. If you want your method to
take only Grain you can declare the method printGrain(G g) like the
declaration below.
public class GenericMethods {
public <G extends Grain> void printGrain(G g){
System.out.println(g.toString());
}
public static void main(String... args) {
GenericMethods
gm = new GenericMethods();
gm.printGrain(new Wheat());
gm.printGrain(new Basmati());
// Compiler error :
// The method
printGrain(G) in the type GenericMethods is
// not applicable for
the arguments (Alphonso)
gm.printGrain(new Alphonso());
}
}
|
The above will ensure that the method takes in the objects
which are of type Grain. You could have declared the same method without a type
and it would have worked too in this scenario. You should actually avoid using
generics where it is not needed. Though a generic code does a type checking
during compilation, it is sometimes cumbersome to read and understand.
public void printGrain(Grain g){
System.out.println(g.toString());
}
|
Remember generics can be used to impose multiple type
checking. If you want to make sure that the Grain which is used in the method
is also exportable, you can use something like this.
public <G extends Grain &
Exportable> void printGrain(G g){
System.out.println(g.toString());
}
|
Since java erases the type after compilation (type erasure coming
in later section) it actually cannot distinguish the above two functions. These
two methods, if part of the same class, would actually give a compiler error - Erasure
of method printGrain(Grain) is the same as another method in type
GenericMethods.
public void printGrain(Grain g){
System.out.println(g.toString());
}
public <G extends Grain &
Exportable> void printGrain(G g){
System.out.println(g.toString());
}
|
Prev Topic
|
Topics
1. Introduction
2. Bounding
8. Target Types
9. Type Erasure
|
|
Comments
Post a Comment