递归泛型

4
有没有一种方法可以使这个方法真正通用,并消除警告?
/**
 * <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);
        }
    });
}

我尝试使用Function<? extends Comparable<?>, T>Function<? extends Comparable<? extends Comparable>, T>,但是都无法编译通过,因为调用 compareTo 方法时出错。对于前者错误信息如下:

在类型 Comparable 中,方法 compareTo(capture#9-of ?) 对参数 (capture#10-of ? extends Comparable) 不适用


你能提供Function类吗? - melihcelik
1
嗨,我正在查看这个问题,但是没有任何进展。但是我有一个评论,不要否定compareTo()的结果,因为如果有人返回Integer.MIN_VALUE,它将保持为Integer.MIN_VALUE,排序顺序将不会是您想要的。相反,当升序为false时,反转调用,例如从a.compareTo(b)b.compareTo(a);。很烦人,我知道... - Grundlefleck
@Grundlefleck 谢谢,我已经实现了。 - Bart van Heukelom
3个回答

5

试试这个:

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);
        }
    });
}

您还需要使用 super 来允许为子类型定义的比较器。更多的解释,请参考这里:http://docs.oracle.com/javase/tutorial/extra/generics/morefun.html 更新 此外,通过查看您的代码,我发现另一个问题是重复造轮子,有一个很好用的库叫做 Google Collections,它提供非常方便的Ordering 概念来解决这个问题。
所以您的代码将会是这样的:
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);

嗯,你仍然有Comparable原始类型... 你可能想要的是? super C,而不是T。在这种情况下,super是一个好主意,但没有其他依赖于C的参数,所以不是必需的。 - Lukas Eder
虽然我不得不将apply更改为call,但它确实起作用了。我已经在使用Google Collections(在Guava中),因此也许我会完全放弃这个函数。虽然对于学习泛型还是有好处的。 - Bart van Heukelom

2
这对我来说没问题(Eclipse编译器)。
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);
    }
  });
}

正如其他人所发表的,您甚至可以进一步声明U

U extends Comparable<? super U>

如果您有更多基于U的方法参数/返回值,那将会很方便。


对我来说,那段代码可以编译通过,但是对那个方法的调用却不行。你能给一个调用的例子吗? - Grundlefleck

1

如果你为函数声明了两个参数会怎样呢?

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  ...

我还没有通过编译器进行自我检查(没有您的接口,而且我太饿了,无法模拟它们 :)),但可以试一下。

我也太昏昏欲睡了,无法理智地思考应该是 C extends Comparable<C> 还是 C extends Comparable<? super C>。我认为前者可以工作并且更加通用,尽管在实践中,大多数类都不会实现 Comparable 接口,除非是针对它们自己。


也许可以使用 <T,C extends Comparable<? super C>> 来实现更通用的解决方案。 - ratchet freak
@ratchetfreak 哈,刚刚编辑了我的答案以添加那个 :). 重要问题:“对于更通用的解决方案”,意在双关语? - yshavit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接