Is there a way to make this method properly generic and do away with the warnings?
/**
* <p>Sort a collection by a certain "value" in its entries. This value is retrieved using
* the given <code>valueFunction</code> which takes an entry as argument and returns
* its value.</p>
*
* <p>Example:</p>
* <pre>// sort tiles by number
*Collects.sortByValue(tileList, true, new Function<Integer,NormalTile>() {
* public Integer call(NormalTile t) {
* return t.getNumber();
* }
*});</pre>
*
* @param list The collection.
* @param ascending Whether to sort ascending (<code>true</code>) or descending (<code>false</code>).
* @param valueFunction The function that retrieves the value of an entry.
*/
public static <T> void sortByValue(List<T> list, final boolean ascending, @SuppressWarnings("rawtypes") final Function<? extends Comparable, T> valueFunction) {
Collections.sort(list, new Comparator<T>() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override public int compare(T o1, T o2) {
final Comparable v1 = valueFunction.call(o1);
final Comparable v2 = valueFunction.call(o2);
return v1.compareTo(v2) * (ascending ? 1 : -1);
}
});
}
I tried Function<? extends Comparable<?>, T>
and Function<? extends Comparable<? extends Comparable>, T>
but neither compiled, with an error on the call to compareTo
. For the former that is:
The method compareTo(capture#9-of ?) in the type Comparable is not applicable for the arguments (capture#10-of ? extends Comparable)
Try this:
public static <T, C extends Comparable<? super C>> void sortByValue(List<T> list, final boolean ascending, final Function<C, T> valueFunction) {
Collections.sort(list, new Comparator<T>() {
@Override public int compare(T o1, T o2) {
final C v1 = valueFunction.apply(o1);
final C v2 = valueFunction.apply(o2);
return v1.compareTo(v2) * (ascending ? 1 : -1);
}
});
}
you also need the super
to allow comparators defined for sub types. More explanations here: http://docs.oracle.com/javase/tutorial/extra/generics/morefun.html
UPDATE
Also, looking at your code I see yet another bicycle, there is a good library the Google Collections, which provides very convenient Ordering notion to handle it.
So, your code would look like:
Ordering<NormalTile> myOrdering = Ordering.natural()
.onResultOf(new Function<Integer,NormalTile>() {
public Integer call(NormalTile t) {
return t.getNumber();
}))
.nullsLast();
...
Collections.sort(list, myOrdering);
//or
newList = myOrdering.sortedCopy(readonlyList);
This works for me (Eclipse compiler)
public static <T, U extends Comparable<U>> void sortByValue(
List<T> list, final boolean ascending, final Function<U, T> valueFunction) {
Collections.sort(list, new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
final U v1 = valueFunction.call(o1);
final U v2 = valueFunction.call(o2);
return v1.compareTo(v2) * (ascending ? 1 : -1);
}
});
}
As others posted, you may even go further and declare U
as
U extends Comparable<? super U>
That will come in handy if you have more method arguments / return values depending on U
What if you declare two parameters for the function?
public static <T,C extends Comparable<C>> void sortByValue(List<T> list,
final boolean ascending, final Function<C, T> valueFunction) {
...
final C v1 = ...
final C v2 ...
Haven't sanity checked myself with a compiler (don't have your interfaces and am too hungry to mock them :) ), but give it a shot.
I'm also too groggy to reason over whether it should be C extends Comparable<C>
or C extends Comparable<? super C>
. I think the former would work and be a tad more general, although in practice, most classes don't implement Comparable except against themselves.
来源:https://stackoverflow.com/questions/8415604/recursive-generics