0

I am working on the following little utility class:

public class Collections2 {
    public static <T, K extends Comparable<? super K>> Comparator<T>
            comparatorBy(Function<T, K> selector) {
        return (a, b) -> selector.apply(a).compareTo(selector.apply(b));
    }

    public static <T, K extends Comparable<? super K>> void
                sortBy(List<T> list, Function<? super T, K> selector) {
        Collections.sort(list, comparatorBy(selector));
    }
}

This allows sorting by python style key functions, so I don't have to write full comparators for every field I want to sort by, instead I can just write:

List<Person> persons = ...;
Collections2.sortBy(persons, Person::getName);

This is nice, I imagine writing lots of utility methods with this style like minBy, maxBy, groupBy, and so on.

But what if somebody want to use these new utility functions with a comparison they already have? I thought of the following:

public static class KeyByComparator<T> implements Comparable<KeyByComparator<T>> {
    private T target;
    private Comparator<? super T> comparator;
    public KeyByComparator(T target, Comparator<? super T> comparator) {
        this.target = target;
        this.comparator = comparator;
    }
    @Override
    public int compareTo(KeyByComparator<T> other) {
        return this.comparator.compare(this.target, other.target);
    }
}

public static <T> Function<T, KeyByComparator<T>> keysByComparator(Comparator<? super T> comparator) {
    return t -> new KeyByComparator<T>(t, comparator);
}

So they could use it this way:

public static final Comparator<Person> LEGACY_COMPARATOR = ...;
// ...

Collections2.sortBy(persons, Collections2.keysByComparator(LEGACY_COMPARATOR));
// Ignore the fact that regular sort would be shorter
// Collections.sort(persons, LEGACY_COMPARATOR)

But to achieve this, I had to make KeyByComparator<T> public.

What if I dont want to expose the KeyByComparator<T> class? Here is what I tried:

public static <T, K extends Comparable<? super K>> Function<T, K>
        keysByComparator2(Comparator<? super T> comparator) {
    return t -> new KeyByComparator<T>(t, comparator);
}

But it gives this compilation error:

Type mismatch: cannot convert from Collections2.KeyByComparator<T> to K

Is there some fancy generic type constraint for return values?

Tamas Hegedus
  • 28,755
  • 12
  • 63
  • 97
  • I thought you could do this already? `Arrays.sort(rosterAsArray, Person::compareByAge);` works. – markspace Dec 05 '15 at 21:55
  • Yes but you had to implement `static int Person compareByAge(Person a, Person b)`. I don't want comparators if not necessary. In my example I sort by supplying a getter instead of a comparator. – Tamas Hegedus Dec 05 '15 at 22:04
  • 3
    You actually don't need any of this. `Collections2.sortBy(persons, Person::getName);` can be written `persons.sort(Comparator.comparing(Person::getName))`. – Tunaki Dec 05 '15 at 22:05
  • Yes, I see now. I'll leave my dumb comment up as a warning to others. What you're doing seems very similar to `Comparator.comparing()` and similar methods. Can you explain how they're different? – markspace Dec 05 '15 at 22:10
  • @Tunaki @markspace hmm you both are right, I wasn't aware of this `comparing` function in the runtime. So you are right, this utility is kinda useless. However the question still stands. – Tamas Hegedus Dec 05 '15 at 22:12
  • This might help (I haven't read it fully myself): http://stackoverflow.com/questions/24436871/very-confused-by-java-8-comparator-type-inference – markspace Dec 05 '15 at 22:13

0 Answers0