10

The below is my code

class NumberComparator<Number> implements Comparator<Number> {
    public int compare(Number o1, Number o2) {
        return 1;
    }
}

public class Ex28 {
    public static void main(String[] args) {
        TreeSet set = new TreeSet(new NumberComparator<Number>());
        set.add(1);
        set.add(1.4f);
        set.add(1L);
        set.add("1a");
        System.out.println(set);
    }
}

As I have defined my own comparator of type Number , but still when I'm adding any thing else that is a string to it, it's not giving me any exception. It's simply working fine. I'm getting the output as

[1, 1.4, 1, 1a]

Can any one please explain why it's happening.

brimborium
  • 9,362
  • 9
  • 48
  • 76
Krushna
  • 5,059
  • 5
  • 32
  • 49

3 Answers3

17

The problem is a mixture of some poor practices:

  • You're using the raw type for TreeSet
  • Your NumberComparator is generic (Number is a type parameter)

The fact that Number is a type parameter here means that type erasure means you won't actually be casting to the real Number type.

If you change your comparator to:

class NumberComparator implements Comparator<Number> {
    public int compare(Number o1, Number o2) {
        return 1;
    }
}

and your calling code to:

TreeSet set = new TreeSet(new NumberComparator());

then I'd expect an exception.

Further, if you change your code to not use the raw type:

TreeSet<Number> set = new TreeSet<Number>(new NumberComparator());

then you'll get a compile-time error instead.

obataku
  • 29,212
  • 3
  • 44
  • 57
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thank you, I understand , That means Number is type (not actually java.lang.Number) So in run time it's become java.lang.Object so there is no class cast exception.But why it became type ?. Still some thing i'm not able to digest. – Krushna Aug 17 '12 at 09:19
  • @KrushnaCh.Dash: What do you mean by "But why it became type ?" ? You were declaring it as a type parameter in NumberComparator. – Jon Skeet Aug 17 '12 at 09:25
  • Ohh, Sorry My mistake , I got now – Krushna Aug 17 '12 at 10:51
  • The comparator implementation is also very broken, and I believe that if it had actually _used_ the Numbers at all (say, `intValue`), you'd probably have gotten an exception from that. – Kevin Bourrillion Aug 24 '12 at 02:11
  • @KevinBourrillion: The comparator is definitely broken, yes - but using the numbers *doesn't* cause a problem. You can't call `intValue` within `compare` though because `Number` doesn't mean java.lang.Number - it means the type parameter `Number`. – Jon Skeet Aug 24 '12 at 05:55
  • 1
    Even better, then: if you had been writing a real comparator instead of a bogus one, you'd have gotten a _compile_ error from trying to do anything with the Number. – Kevin Bourrillion Aug 28 '12 at 17:44
3

A Comparator for a TreeSet is used for ordering, not for throwing CCEs. Since your comparator is designed to return 1 for everything, it means that the ordering wouldn't be right.

That is the reason your output is not ordered.

Be sure to read the documentation of the constructor of TreeSet.

/**
 * Constructs a new, empty tree set, sorted according to the specified
 * comparator.  All elements inserted into the set must be <i>mutually
 * comparable</i> by the specified comparator: {@code comparator.compare(e1,
 * e2)} must not throw a {@code ClassCastException} for any elements
 * {@code e1} and {@code e2} in the set.  If the user attempts to add
 * an element to the set that violates this constraint, the
 * {@code add} call will throw a {@code ClassCastException}.
 *
 * @param comparator the comparator that will be used to order this set.
 *        If {@code null}, the {@linkplain Comparable natural
 *        ordering} of the elements will be used.
 */
public TreeSet(Comparator<? super E> comparator) {
    this(new TreeMap<>(comparator));
}

It clearly states that if you try to add any other element other than the ones the Comparator is designed for, it would throw a ClassCastException. You could simulate this if you don't use generics by trying to add a String. If you do use generics, however, this would be just a compile time issue.

Meanwhile, you should be using generics consistently.

class NumberComparator<C> implements Comparator<C> {
    public int compare(C o1, C o2) {
        return 1; // change this logic
    }
}

Set<Number> set = new TreeSet<>(new NumberComparator<Number>());
adarshr
  • 61,315
  • 23
  • 138
  • 167
0

All said , you would get you class cast exception if you define your comparator as below :)

import java.util.Comparator;

class NumberComparator<Number> implements Comparator<java.lang.Number> {
public int compare(java.lang.Number o1, java.lang.Number o2) {


    return 1;
}
}
  • After this change there is no point in making the class parametrized, so rename it to just `NumberComparator` instead of `NumberComparator`. – dolmen Aug 29 '12 at 09:42