Search code examples
javagenerics

Using generics to create a node traversal class?


Does anyone know the best way to approach this problem?

I'm getting the following error, which I assume means I need a different approach to compare the data element of the node.

java: bad operand types for binary operator '<'
  first type:  V
  second type: V

Code:

class Node<V> {
    Node<V> left;
    Node<V> right;
    V data;

    public Node(V d) {
        left = null;
        right = null;
        data = d;
    }

    public void Insert(V d) {
        if (data != null) {
            if (d < data) {
                if (left == null) {
                    left = Node(d);
                } else {
                    left.Insert(d);
                }
            }
        }
    }
}

Solution

  • As already suggested in the comments by @Dan W, the generic type V in the Node class should define an upper bound to Comparable in order to restrict the possible types in place of V to only the ones that can be compared (ordered).

    In Java, the operators <, >, <=, >= can be used only for numeric primitive types, such as int, float, double, and so on. Instead, to compare reference types, their class needs to implement the Comparable interface to define a natural ordering among their elements.

    This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.

    Most Java classes like Integer, Float, Double and many others already implement the Comparable interface, while for your own classes you need to provide the comparison logic within the compareTo method. Here is also a great article from Bealdung with a guide on how to implement the Comparable interface.

    In your case, a generic version of your class Node can be re-written in the following way:

    class Node<V extends Comparable<? super V>> {
    
        Node<V> left;
        Node<V> right;
        V data;
    
        public Node(V d) {
            left = null;
            right = null;
            data = d;
        }
    
        public void Insert(V d) {
            if (data != null) {
                if (d.compareTo(data) < 0) {
                    if (left == null) {
                        left = new Node<>(d);
                    } else {
                        left.Insert(d);
                    }
                }
            }
        }
    }
    

    In the previous snippet, the type parameter for Comparable is written with a lower bounded wildcard (Comparable<? super V>) instead of just the generic parameter V (Comparable<V>), so that classes that do not implement directly the Comparable interface can be accepted as well when their supertypes actually do. This is the case, for example, for the LocalDate class, which does not implement Comparable directly, but its supertype ChronoLocalDate actually does. Like so, also LocalDate instances can be accepted and confronted.

    A particular thank you to @Holger for pointing this out in the comments.