Java Articles

Advertisement

Java Generic Method Example

by Ram Satish

Share

In our previous article, we have seen an example of a generic type. In this article, I will show you that methods can be generic too.

Static and non-static methods as well as constructors can have type parameters.

In our last article, we have seen how to define a generic type. As a quick reminder, here is the code snippet:

public class Node<V> {
...
}

Note that <V> is the generic type declared right after the class name. The declaration of the formal type parameters in methods is similar to the syntax for generic types.
The type parameter is delimited by angle brackets and appears before the method’s return type. We will be seeing an example of it when we write a static method.

Advertisement

Its syntax is identical to the type parameter list which is of  generic type. If more than one type, they must be separated by commas.

We will first enhance our Node class by adding two new attributes, next and previous nodes. Both these attributes are Node themselves so we declare them of type Node<E>.

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

public Node(E value) {
mValue = value;
}

public E getValue() {
return mValue;
}

public Node getNext() {
return mNext;
}

public Node getPrevious() {
return mPrevious;
}
}

I will add a new instance method linkAfter() to link two nodes. I will first add it as a non-generic method so that we can come across the issues and then work upon them.

    public void linkAfter(Node node) {
node.mPrevious = this;
node.mNext = mNext;
if (mNext != null) {
node.mNext.mPrevious = node;
}
mNext = node;
}

There is no issue with the above code, except that compiler warns us:
Type safety: The field mPrevious from the raw type Node is assigned a value of type Node. References to generic type Node should be parameterized

In the test below, I am showing you how to link nodes using linkAfter(). Note that my nodes are integer based. I create two integer nodes one and two and then link node one to node two such that node two follows node one.

        
public void testLinkOneAndTwo() {
Node one = new Node(1);
Node two = new Node(2);
one.linkAfter(two);
assertEquals(one, two.getPrevious());
assertEquals(two, one.getNext());
}

If I want to know the total value of the node in link ==> next.value + current node value + previous.value, my method would be:

    public void testNodeValue() {
Node one = new Node(1);
Node two = new Node(2);
one.linkAfter(two);
Integer totalValue = one.getValue() + two.getValue();
assertEquals(3, totalValue.intValue());
}

This is just a test case, I want this implementation to be a part of the Node itself. But we can’t add this implementation to our Node class as the value it is holding is of a generic type. It can represent a String or an Integer or some CustomClass type. Adding an integer node and a string node won’t make any sense. We need a Node type that represents a number Node.

One way would be to extend Node where a Number type would substitute <E> and then add calculateLinkValue() to it.

How do we extend the Node class and replace the type argument <E> with our Integer argument value?

Let us do something simple.

public class IntNode extends Node<E> {
...
}

The constructor Node(E) refers to the missing type E
What went wrong? We have substituted the generic type with argument value <E> but <E> is not declared. Note that if we have to use any generic type, we first need to declare it right after the class name within the angular brackets. Just to gain clarity, we now declare <E> and check whether it compiles.

public class NumberNode<E> extends Node<E>  {
public NumberNode(E value) {
super(value);
}
}

This now compiles fine but its not we want. We want an Integer type node so we don’t have to declare a new generic type, all we have to do is substitute Integer type in Node<>.

public class NumberNode extends Node<Integer> {

public NumberNode(Integer value) {
super(value);
}
}

The above class looks fine but if we want to create a long node and link it with integer node, we won’t be able to do it. If we want the NumberNode to be a number node and not just limited to a Integer Node, we will have to replace the type with type Number.

public class NumberNode extends Node  {

public NumberNode(Number value) {
super(value);
}
}

If we have a node containing a Double value, we expect getValue() to return value of type Double but it returns Number so below code fails to compile.

    public void testGetValue() {
Double d = 3.2d;
NumberNode doubleNode = new NumberNode(d);
Double valueInDouble = doubleNode.getValue();
}

Type mismatch: cannot convert from Number to Double
If we want the node.getValue() to return the exact type then we have to declare the generic type to be an extension of Number like below.

public class NumberNodeNode<N extends Number> extends Node<N> {
public NumberNode(N value) {
super(value);
}
}

NumberNode has declared a new generic type that extends Number. This is called a bounded type. The generic type can be of any type that extends Number. Since it is bounded, in this case, by Number type, it is called an upper bounded type.
Now we can add our new method to the NumberNode class to calculate link value.

public class NumberNode<N extends Number> extends Node {

public NumberNode(N value) {
super(value);
}

public int calculateLinkValue() {
int totalValue = getValue().intValue();
if (getNext() != null && getNext().getValue() != null) {
totalValue += getNext().getValue().intValue();
}
if (getPrevious() != null && getPrevious().getValue() != null) {
totalValue += getPrevious().getValue().intValue();
}
return totalValue;
}

}

Below is the test case that calculates the node value:

    public void testNodeValue() {
NumberNode one = new NumberNode(1);
NumberNode two = new NumberNode(2);
one.linkAfter(two);
assertEquals(3, one.calculateLinkValue());
}

The implementation looks good but there is still one caveat. Remember we have added linkAfter() as a non-generic method. There is bug lingering underneath the code. Since linkAfter() is non-generic, it will allow us to add even non-numeric nodes.

    public void testNodeValueWithStringNode() {
NumberNode one = new NumberNode(1);
Node two = new Node("Two");
one.linkAfter(two);
assertEquals(3, one.calculateLinkValue());
}

With a non-numeric node, invoking calculateLinkValue() will throw a runtime exception java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

Let us re-factor linkAfter() method to make it generic, instead of Node, now it takes the Node<E> object to be linked.

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

Once we re-factor the method to be generic, linking string node to a number node is no more allowed, compilation fails with below error:
The method linkAfter(Node<Integer>) in the type Node<Integer> is not applicable for the arguments (Node<String>)

This is highly desirable as the bug has simply shifted from runtime to compile time. This is one of the benefits of generics, we can fix issues at the compile time itself.

In this article, we learnt how to write a non-static generic method. We have also learnt how to create an upper bounded generic type that extends a pre-defined type. In the next article, I will show you how to write a static generic method.

Download the code here.

Share

Advertisement

Related

Advertisement

Latest

Advertisement