Static Generic Method Example

0

Just like type declarations, method declarations can be generic too. In this article, I will show you an example of static generic method. In one of my previous articles, I have already shown you an example of a non-static generic method.

In all my articles on java generic, I have used Node class as an example. Here is the Node class.

public class Node<E> {
    E mValue;
    Node<E> mNext;
    Node<E> mPrevious;

    public Node(E value) {
        mValue = value;
    }
    
    public void linkAfter(Node<E> node) {
        validate(node);
        node.mPrevious = this;
        node.mNext = mNext;
        if (mNext != null) {
            node.mNext.mPrevious = node;
        }
        mNext = node;
    }

    public E getValue() {
        return mValue;
    }

    public Node<E> getNext() {
        return mNext;
    }

    public Node<E> getPrevious() {
        return mPrevious;
    }               
}

In the example, I have three number nodes, 1->2->3, connected together.
My task would be to convert the number nodes to word nodes. Once converted, I want the nodes to look like, one->two->three.

First I will implement this in non-generic fashion and write a small test to make sure it works.

Download code here.

For now, let’s not bother too much about the implementation. My new method that converts the nodes is called convert(). It is a static method. What is important to note is, it takes in a Map of values to map a number to its word value. The second parameter is the number node to be converted.

Node is an immutable object so we will have to create a new node for each old node and then establish the links the way old node has.

    public static Node convert(Map valueMap, Node node) {
        Node firstNode = null;
        Map convertedNodes = new HashMap();
        while(node != null) {                
            Object o = valueMap.get(node.getValue());     
            Node newNode = (Node) createNode(convertedNodes, node.getValue(), o);
            if (firstNode == null) {
                firstNode = newNode;
            }
            setNode(newNode, NodeType.NEXT_NODE, valueMap, convertedNodes, node);
            setNode(newNode, NodeType.PREV_NODE, valueMap, convertedNodes, node);
            node = node.getNext();
        }
        return firstNode;
    }

Since the methods are non-generic, there are lots of compiler warnings:
There is a compiler warning at the statement that calls the convert() and assigns the result to Node<String>
Node<String> nodeOne = NodeUtils.convert(valueMap, one);
Type safety: The expression of type Node needs unchecked conversion to conform to Node<String>

It would be nice to if convert(), returns Node for the type we want the node to be converted to.
Let us work on convert() method to make it generic.

What is a generic method?

The syntax for declaration of generic method is similar to the syntax for generic types. The type parameter section is delimited by angle brackets and appears before the method’s return type. If there are more than one type parameter, they are comma separated.

Here is the example of generic convert() method.

        public static <K, V> Node<V> convert(Map<K, V> valueMap, Node<K> node) {
        Node<V> firstNode = null;
        Map<K, Node<V>> convertedNodes = new HashMap<K, Node<V>>();
        while(node != null) {                
            V o = valueMap.get(node.getValue());     
            Node<V> newNode = createNode(convertedNodes, node.getValue(), o);
            if (firstNode == null) {
                firstNode = newNode;
            }
            setNode(newNode, NodeType.NEXT_NODE, valueMap, convertedNodes, node);
            setNode(newNode, NodeType.PREV_NODE, valueMap, convertedNodes, node);
            node = node.getNext();
        }
        return firstNode;
    }

The method has two type parameters name K and V. K is the placeholder for the first type of node and V is the place holder for the next type to which the first type will get converted to.

How we invoke a generic method?

Test case:
In my test case, I have three integer nodes connected to each other. Next, I create a value map that maps an integer to word.
I then call convert(valueMap, one) passing valueMap and the number node that I want to convert.

    public void testNodeConvert() {
        Node<Integer> one = new Node<Integer>(1);
        Node<Integer> two = new Node<Integer>(2);
        Node<Integer> three = new Node<Integer>(3);
        one.linkAfter(two);
        two.linkAfter(three);
        
        NodeUtils.printNode(one);
        
        Map<Integer, String> valueMap = new HashMap<Integer, String>();
        valueMap.put(1, "one");
        valueMap.put(2, "two");
        valueMap.put(3, "three");        
        Node<String> nodeOne = NodeUtils.convert(valueMap, one);       
        
        System.out.println("Converted to: ");
        NodeUtils.printNode(nodeOne);       
    }

In our example, the compiler would automatically invoke an instantiation of the convert() method with the type arguments Integer and String.
the formal type parameter K is replaced with Integer and V is replaced by type String. The compiler automatically infers that at compile time, the return type Node<V> must be replaced by Node<String>.

Once the method is gerenric, the compiler warning disappears, as the compiler infers that convert() would return Node<String> at compile time.

Another method to look at is the print node method.

    public static void printNode(Node<?> node) {
        while(node != null) {
            System.out.print(node.getValue());
            if (node.getNext() !=null) {
                System.out.print("->");
            }
            node = node.getNext();
        }
    }
}

Notice that we use a wildcard type <?>. Question is why have we not use a formal parameter type like <E>:

public static  void printNode(Node node) {
...
}

We can use a formal parameter and is perfectly legal. However, the type parameter E is used only once. The return type is void and is not dependent on the type parameter nor any other argument in the method. In this case, we just have one argument. The type parameter’s purpose here is to allow a variety of actual argument values to be used while invoking the printNode(). If that is the case, one should use wildcard which means it can take any type.

Output of the test case when run:

1->2->3Converted to: 
one->two->three

Download code javagen.zip

About Author

Ram's expertise lies in test driven development and re-factoring. He is passionate about open source technologies and loves blogging on various java and open-source technologies like spring. You can reach him at [email protected]

Leave A Reply