blurredd
blurredd

Reputation: 185

Casting Comparator to work for a subclass

In practice, is it safe to do the following?

public static <T> Comparator<T> downcast(final Comparator<? super T> orig) {
    @SuppressWarnings("unchecked")
    Comparator<T> casted = (Comparator<T>) orig;
    return casted;
}

Here's a contrived example of its usage:

public static <T> Comparator<T> chainComparators(final Comparator<? super T> a,
        final Comparator<? super T> b) {
    if (a == null) {
        return downcast(b);
    }
    if (b == null) {
        return downcast(a);
    }
    return new Comparator<T>() {
        @Override
        public int compare(T o1, T o2) {
            int i = a.compare(o1, o2);
            if (i == 0) {
                i = b.compare(o1, o2);
            }
            return i;
        }
    };
}

public static Comparator<Integer> ODD_FIRST_COMPARATOR = new Comparator<Integer>() {
    @Override
    public int compare(Integer i1, Integer i2) {
        boolean isEven1 = (i1.intValue() % 2) == 0;
        boolean isEven2 = (i2.intValue() % 2) == 0;
        return Boolean.compare(isEven1, isEven2);
    }
};

public static Comparator<Number> ABS_NUMBER_COMPARATOR = new Comparator<Number>() {
    @Override
    public int compare(Number n1, Number n2) {
        double d1 = Math.abs(n1.doubleValue());
        double d2 = Math.abs(n2.doubleValue());
        return Double.compare(d1, d2);
    }
};

public static void main(String[] args) {
    Comparator<Integer> comp = null;
    comp = chainComparators(comp, ODD_FIRST_COMPARATOR);
    comp = chainComparators(comp, ABS_NUMBER_COMPARATOR);

    List<Integer> list = new ArrayList<Integer>();
    list.add(-42);
    list.add(-23);
    list.add(-15);
    list.add(-4);
    list.add(8);
    list.add(16);

    Collections.sort(list, comp);
    System.out.println(list); // prints [-15, -23, -4, 8, 16, -42]
}

I know I could make chainComparators() return Comparator<? super T> instead of using downcast(), or I could change all of the code to not use or explicitly check for null Comparators (which also removes the need to use downcast), but neither of these changes seem to be worth the effort for a large codebase. Is there any reasonable situation where either downcast() or chainComparators() will fail?

Upvotes: 3

Views: 1607

Answers (2)

newacct
newacct

Reputation: 122429

If you don't want the unchecked warning, you can always do it this way:

public static <T> Comparator<T> downcast(final Comparator<? super T> orig) {
    return new Comparator<T>() {
        @Override
        public int compare(T o1, T o2) {
            return orig.compare(o1, o2);
        }
    };
}

Upvotes: 3

Cephalopod
Cephalopod

Reputation: 15145

It should be safe, because Comparator is a consumer, which means that instances of T are only passed as arguments, and never returned.

As far as I can see, your code is good.

Upvotes: 1

Related Questions