Tuesday, October 4, 2011

Java Generics Part - 3


Writing Generic Methods

In the previous section, we saw how to write Parameterized Classes. Now, let us spend time in exercising Parameterized Methods or Generic methods in this section. 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.
Following code snippet shows how to declare a Generic method.
GenericMethods.java
package generics.methods;
 
public class GenericMethods
{
    static <T> void printType(T anyType)
    {
        System.out.println(anyType.getClass().getName());
    }
 
    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 (<T>).
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 aGgenericMethod(A aType, B bType)
{
    // Something here.
}
But the same type parameter can de used multiple number of times in the parameter list. For example, the type paramter A is defined once but used multiple times in the following code,
<A, B> void aGgenericMethod(A aType, A anotherType, B bType)
{
    // Something here.
}

Wildcards

The following sections explain the usage of Wild-card character in Generics. For example, consider the following,
List<String> strObjects = new ArrayList<String>();
The above line declares an Array List with a type being the String type. The declaration says that the list (strObjects) in this case, can hold objects only of type java.lang.String. No other type is permitted other than java.lang.String. So, the following statements are legal as they are adding objects of type java.lang.String.
strObjects.add(new String("A String"));
strObjects.add(new String("Another String"));
Now, consider the following assignment statement which assigns the object reference strObjects to anotherStrObjects. Now anotherStrObjects is pointing to strObjects reference.
List<String> anotherStrObjects = strObjects;
The above is a perfectly legal statement as the assignment happens between the same type. Now, consider the following,
List<Object> objects = strObjects;
The above statement will lead to a compiler error telling that "Cannot convert from List<String> to List<Object". Though String is a sub-class of the Object class, this type of conversion is not possible in Generics, but the following code compiles well.
Object someObject = new String("");
The reason why it is not possible to assign the List containing strings to a List that can hold Objects can be given justification as follows. The main goal of having Generic code in a program is to ensure type-safety. It means that the expected Application types and the types sent by the Clients should match. There should not be any deviation or mis-match in their types. If we go back to the code, on the left-hand side, we have a List that can hold Objects represented by List<Object> objects. This tells to the compiler that this List can hold objects of type java.lang.Object. Now we are making this object List to point to some other list whose parametric type is String. Though String is a concrete sub-class of Object, this assignment is not possible.
Let us see how to get over this problem. Consider the following code,
List<?> objects = strObjects;
The character '?' is a wild-card character and it stands for any Java type. It can be java.lang.Object type or some other type. It is just a place-holder that tells that it can be assigned with any type. Considering this case, the following are now valid syntaxes.
List<?> anyObjects = null; 
                       
List<Integer> integers = new ArrayList<Integer>();
anyObjects = integers;
               
List<Double> doubles = new ArrayList<Double>();
anyObjects = doubles;
The above code snippet tries to assign a list of integers and a list of doubles to the reference anyObjects. This is perfectly legal. Another strange thing has to be considered is while adding elements into the collection. Since the type parameter for the reference anyObjects is '?', no objects can be added to the collection, not even java.lang.Object. The only exception is that only null can be added to these type of collection.
anyObjects.add(new Integer(1)); // Wont compile
anyObjects.add(new Double(1.0)); // Wont compile
 
anyObjects.add(null); // This will compile.

No comments: