Java Generics - 4. Generic Methods

This is part 4 of the 9 part series on Java Generics 


Prev Topic

Topics



Next Topic


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



Next Topic

Comments

Popular posts from this blog

Java Generics - 7. Upper and Lower Bounds

Java Functional Programming : 3. The java.util.function Library

Java Generics - 3. Multiple bounding