Reputation: 1966
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?
Upvotes: 3
Views: 454
Reputation: 46861
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; }
// ...
}
Upvotes: 8