Java Generics - 6. Typed Classes as method parameters

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



Prev Topic

Topics



Next Topic



Java Generics - Typed Classes as Method Parameters









We saw in the previous section, that List which is a typed interface can be used either with the wildcard (?) or with a defined type E, ‘E extends Number’ in this case. Can we use it with a Fruit that is exportable? Why not, the rules are same. Here is a method which gives the maximum price for all Exportable fruits in a list.


public static <E extends Fruit & Exportable> double maxPrice1(List<Bean<E>> beans){
       double max = 0;
       for (Bean<E> b : beans){
              E e = b.get();
              if (e.exportPrice() > max)
                     max = e.exportPrice();
       }
       return max;
}


Can you do the same thing with the wildcard? Can you write “<E extends Fruit & Exportable>within the bean diamond? It appears you cannot, and this is another reason why I prefer the declaration with E rather than with a wildcard. There are certain situations where wildcards have an edge and we will discuss that later. In this case the reference defined by E has the access to both the properties of a Fruit and the interface Exportable. This is yet another reason to prefer this declaration over a wildcard.


public static double maxPrice2(List<Bean<? extends Fruit & Exportable>> beans){
       double max = 0;
       for (Bean<? extends Exportable> b : beans){
              Exportable e = b.get();
              if (e.exportPrice() > max)
                     max = e.exportPrice();
       }
       return max;
}


This above declaration would give a compiler error - Incorrect number of arguments for type Bean<E>; it cannot be parameterized with arguments <? extends Fruit, Exportable>. This is because when referring the bean within the method, you will not be sure which type should be used when referencing. Java disallows such ambiguity. You can use either Fruit or Exportable, but not both. The code below would be the correct one to achieve the same functionality.  


       public static double maxPrice2(List<Bean<? extends Exportable>> beans){
              double max = 0;
              for (Bean<? extends Exportable> b : beans){
                     Exportable e = b.get();
                     if (e.exportPrice() > max)
                           max = e.exportPrice();
              }
              return max;
       }


In the first case we have assigned the reference of Fruit and Exportable to E. But in the second one we are unsure what should be used as a reference and that is why java disallows it. How does java tackle it internally, is a question we will explore in the section for type erasure. Use E when you want to use the reference inside the method and wildcard when you don’t have to.
One of the common misunderstandings in java programming language is to think if a method takes in a defined type as a parameter, you can pass in the subclass of the type. This is infact incorrect. Have a look at the method below which for which the input parameter is a Bean<Wheat>.


       public static void printGrain(Bean<Wheat> w){
              System.out.println(w);
       }


Remember our grain hierarchy where - Wheat extends Grain. The two sub classes of Wheat are Durum and Farro. If you try to invoke the printGrain method with Bean<Durum> or Bean<Farro> you would actually get a compiler error – something like - The method printGrain(Bean<Wheat>) in the type TypedParameters is not applicable for the arguments (Bean<Durum>). Though intututive, java prohibits this kind of behavior.


       Bean<Wheat> w = new Bean<>();
       // this is valid
       printGrain(w);

       Bean<Durum> d = new Bean<>();
       // compiler error
       printGrain(d);


In case you want to use all the subclasses of the types in the method, modify the method to use wildcards as below. Both the printGrain statements above would then be valid.


       public static void printGrain(Bean<? extends Wheat> w){
              System.out.println(w);
       }


However a method which takes in List<Bean<Wheat>> as a parameter can also take an instance of ArrayList<Bean<Wheat>> as input value. This is consistent with our java programming even without a typed class.



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