Nishant123 Nishant123 - 6 months ago 17
Java Question

Shadowing of type parameters

I have a very basic question regarding type parameters which I came across while solving a problem.

I have a class with a string array which I am sorting using a custom Comparator class. Below is my Comparator class

class SortComparator implements Comparator<String>
{

@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return o1.compareTo(o2);
}

}


The above class runs fine and outputs the desired result.

If the class name is changed from
SortComparator
to
SortComparator<String>
the compare method needs to be changed. The class after changes looks like this

class SortComparator<String> implements Comparator<String>
{

@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return ((java.lang.String) o1).compareTo((java.lang.String) o2);
}

}


The above class also runs fine and outputs the desired result

Why is there a need to explicitly cast the
String
objects
o1
and
o2
to
java.lang.String
when they already are objects of the same class?


I searched for this query of mine for some time on net and found a very similar question

Comparing two Integers with my own Comparator

The answer to this question says that it is because of
shadowing
.

I know shadowing in terms of local variables, instance variables and class variables but how does it work in this case?

Answer

The problem is with generic type that is called String. It's collision between generic type parameter and actual class String.

Because the type parameter String is unbounded, the Java compiler replaces it with Object hence arguments of method compare works as Object and object class doesn't have compareTo method hence you have to cast.

Try below example that works fine.

class SortComparator<T> implements Comparator<String>
{

    @Override
    public int compare(String o1, String o2) {
        // TODO Auto-generated method stub
        return  o1.compareTo(o2);
    }

}

Please have a look at Java documentation on Erasure of Generic Types

During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded.

Below Example copied directly from above Java documentation for more clarity.

Consider the following generic class that represents a node in a singly linked list:

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) }
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

Because the type parameter T is unbounded, the Java compiler replaces it with Object:

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}