Java Generics - 6. Typed Classes as method parameters
This is part 6 of the 9 part series on Java Generics
Prev Topic
|
Topics
1. Introduction
2. Bounding
8. Target Types
|
|
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
1. Introduction
2. Bounding
8. Target Types
9. Type Erasure
|
|
Comments
Post a Comment