4

So here is my code:

public class Demo {

    public static final Comparator<String> SORT_BY_LENGTH = new SortByLength();

    private static class SortByLength implements Comparator<String>{
        public int compare(String w, String v) {
            return w.length()-v.length();
        }
    }

    public static void main(String[] args) {
        Object o1 = "abc", o2 = "bc";
        Comparator c = SORT_BY_LENGTH;
        System.out.println(c.compare(o1, o2));//OK, output 1
    }
}

So what confuses me is that the signature of the compare() method takes 2 String variables as argument. However, even when I input 2 arguments of Object type, it still works. Why is that?

PS: if I define some ordinary method as follows, then the compiler will complain there is an error, because Object cannot be converted to String.

public static int foo(String w, String v) {
    return w.length()-v.length();
}

public static void main(String[] args) {
    Object o1 = "abc", o2 = "bc";
    System.out.println(foo(o1, o2));// compiling error!
}
maycui
  • 43
  • 3
  • basically: because it is an inherited/implemented method, and the original class/interface can not possibly know which types will (come to) exist, so they are defined as Object, since every instance of any class will, in fact, be an Object – Stultuske Apr 06 '18 at 07:10
  • 1
    Related: https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it – JB Nizet Apr 06 '18 at 07:11
  • 3
    Because you are using `Comparator` and not `Comparator` as the type. That way there will be no compile time check (well there is but the type is now `Object` and not `String`. If you change `Comparator c =` to `Comparator c = ` it will work. – M. Deinum Apr 06 '18 at 07:12
  • 2
    change `Comparator c = SORT_BY_LENGTH;` to `Comparator c = SORT_BY_LENGTH;` and see what happens – Arun Sudhakaran Apr 06 '18 at 07:16

1 Answers1

9

You can use Object as arguments here because you used the raw type of Comparator, which is something that you generally should not do.

Here is where you did it:

Comparator c = SORT_BY_LENGTH;

See how there are not generic arguments to Comparator? That is a sign of using raw types. You can assign any instance of a parameterised type, like SORT_BY_LENGTH to a raw type. However, since the raw type Comparator has no generic parameters, its compare method takes two Objects instead of Strings. So you can theoretically pass anything into compare.

However, if the wrong type of object goes into the method, an exception will be thrown. Try putting two new Object() in there!

This is why you should not assign parameterised types to raw types. They are not type safe. You should do something like this instead:

Comparator<String> c = SORT_BY_LENGTH;

or use SORT_BY_LENGTH directly.

If you made the above change, the call to compare will not compile anymore, because it is expecting Strings. You should change the type of o1 and o2 to String.

Sweeper
  • 213,210
  • 22
  • 193
  • 313