Showing posts with label generic-method. Show all posts
Showing posts with label generic-method. Show all posts

Tuesday, 19 April 2011

Expressing dependencies in type parameters : Wildcards vs bounded type parameters

This article explains When should I use generic methods, and when should I use wildcard types?
To understand the answer, let’s examine a few methods from the Collection libraries. It has to methods :
containAll and addAll .

Using wildcard:

interface Collection<E> {
public boolean containsAll(Collection<?> c);
public boolean addAll(Collection<? extends E> c);
}

Using Bounded type parameters
We could have used generic methods here instead:
interface Collection<E> {
public <T> boolean containsAll(Collection<T> c);
public <T extends E> boolean addAll(Collection<T> c);
// hey, type variables can have bounds too!
}

However, in both containsAll and addAll, the type parameter T is used only once.
The return type doesn’t depend on the type parameter, nor does any other argument to the method (in this case, there simply is only one argument).

This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites. If that is the case, one should use wildcards. Wildcards are designed to support flexible subtyping, which is what we’re trying to express here.

Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn’t such a dependency, a generic method should not be used.


It is possible to use both generic methods and wildcards in tandem. Here is the method Collections.copy():

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src){...}
}

Note the dependency between the types of the two parameters. Any object copied from the source list, src, must be assignable to the element type T of the destination list, dst. So the element type of src can be any subtype of T - we don’t care which. The signature of copy expresses the dependency using a type parameter, but uses a wildcard for the element type of the second parameter.

We could have written the signature for this method another way, without using wildcards at all:
class Collections {
     public static <T, S extends T>
     void copy(List<T> dest, List<S> src){...}
}

This is fine, but while the first type parameter is used both in the type of dst and in the bound of the second type parameter, S, S itself is only used once, in the type of src - nothing else depends on it. This is a sign that we can replace S with a wildcard.

Using wildcards is clearer and more concise than declaring explicit type parameters, and should therefore be preferred whenever possible. Wildcards also have the advantage that they can be used outside of method signatures, as the types of fields, local variables and arrays.
Here is an example on this. Returning to our shape drawing problem, suppose we want to keep a history of
drawing requests. We can maintain the history in a static variable inside class Shape, and have drawAll() store its incoming argument into the history field.

static List<List<? extends Shape>> history = 
new ArrayList<List<? extends Shape>>();
public void drawAll(List<? extends Shape> shapes) {

history.addLast(shapes);
for (Shape s: shapes) {
s.draw(this);
}

}

Generic method : How generic are they ?

Beginner's mistake of using Object as parameter
Consider writing a method that takes an array of objects and a collection and puts all objects in the array into the collection. So we try to implement it .

Here is a first attempt:

static void fromArrayToCollection(Object[] a, Collection<?> c) {
for (Object o : a) {
c.add(o); // compile time error

}
}

By now, you will have learned to avoid the beginner’s mistake of trying to use Collection<Object>.
static void fromArrayToCollection(T[] a, Collection<T> c) {
for (Object o : a) {
c.add(o); // works fine this time

}
}


Now we can have method as :

Object[] objectArray = new Object[100];
Collection<Object> collObject = new ArrayList<Object>();
fromArrayToCollection(objectArray , collObject );// T inferred to be Object 
 
String[] stringArr = new String[100];

Collection<String> collString = new ArrayList<String>();
fromArrayToCollection(stringArr , collString );// T inferred to be String
fromArrayToCollection(stringArr , co);// T inferred to be Object

Integer[] ia = new Integer[100];
Float[] fa = new Float[100];
Number[] na = new Number[100];
Collection<Number> cn = new ArrayList<Number>();

fromArrayToCollection(ia, cn);// T inferred to be Number
fromArrayToCollection(fa, cn);// T inferred to be Number
fromArrayToCollection(na, cn);// T inferred to be Number
fromArrayToCollection(na, co);// T inferred to be Object
fromArrayToCollection(na, cs);// compile-time error


Also see here for clearer picture on Using type parameter as Object.
Notice that we don’t have to pass an actual type argument to a generic method. The compiler infers the type argument for us, based on the types of the actual arguments. It will generally infer the most specific type argument that will make the call type-correct.

See this for more:
Expressing dependencies in type parameters : Wildcards vs bounded type parameters



Monday, 18 April 2011

Writing generic methods

A Generic class containing a type parameter affects the entire class, but a generic method containing one or more type parameters affects that particular method only. So it means that a non-generic class can contain a mixture of generic and non-generic methods.


Example:

public class GenericMethods
{
static <T> void printType(T anyType)
{
System.out.println(anyType.getClass().getName());
} 
 
//using generic method
public static void main(String[] args) 
{
GenericMethods.printType(String.class);
GenericMethods.printType(new String(""));
}
}

If we look at the way in which a Generic method is declared, we find that the static method printType() has a return type void and it takes a single parameter called T. Here T stands for any parametric type which can be substituted with any of the Java types by the Clients. Since we have introduced a parameter T, it should be defined. But where? It should be defined in the method definition itself just before the return type of the method ().

The moral is whenever we have different type parameters in a method, it should be defined in the method definition. For example, consider the following method that has two type parameters A and B and they are defined before the return type of the method separated by commas.


<A, B> void genericMethod2Parameters(A aType, B bType)
{
// Something here.
}


Wednesday, 27 October 2010

Upper Bounded Wildcards in generics

Suppose we want to write a generic method which takes a list and print it only when it contains elements subclassing one particular class. So here we need upper bound.
It is possible to set the upper bound of the wildcard like this:
List<? extends Vehicle> vehicles = new ArrayList<? extends Vehicle>();    

In this example I have specified the upper bound to be the class Vehicle. I can now define the printElements() method like this:

public void printElements(List<? extends Vehicle> elements){
for(Vehicle o : elements){
System.out.println(o);
}
}

As you can see it is now safe to cast each element in the list to a Vehicle, as it is done by the new for loop inside the method.
Furthermore, it is now safe to call the method using a List<Car> instance, provided that Car extends Vehicle. Here is an example:
List<Car> elements = new ArrayList<Car>
// ... add Car elements to the list.

printElements(elements);

But, even when using a wildcard with an upper bound it is not safe to write to the List. After all, a Car is always a Vehicle, but a Vehicle is not always a Car.

The type parameterization <? extends E> is called an "upper bounded wildcard" because it defines a type that could be any type so long as it is bounded by the superclass E. It provides covariant relationship such that the referenced object's (eg. Car ) type parameter is a subclass ofVehicle's type parameter