我编写了一个小型列表排序器,它通过使用Key
个对象来提取特定的"键"来自可比形式的物体。然后,分拣机依次按照所有键对列表进行排序。
分拣机可以使用任何键来对给定类型的对象进行排序。每个键都能够处理一种类型的对象,并始终返回相同类型的可比较值。
class Sorter {
public interface Key<T, V extends Comparable<V>> {
public V get(T t);
}
static <T> void sort(List<T> data, final Key<T, ?> key[], final int dir[]) {
Collections.sort(list, new Comparator<T>() {
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
final Comparable av = key[i].get(a), bv = key[i].get(b);
final int cmp = av.compareTo(bv);
if (cmp != 0) return cmp * dir[i];
}
return 0;
}
});
}
}
因此,举例来说,你可以有一个JSONStringKey
来提取String
(Comparable
),你可以有一个单独的JSONNumericKey
来提取{ {1}})(也是Double
)。永远不会比较来自两个不同键的值,但会比较两个不同对象中的相同键。
Comparable
Java在分拣机中警告这一行:
class JSONStringKey extends Sorter.Key<JSONObject, String> {
final String key;
JSONStringKey(String key) {this.key = key;}
public String get(JSONObject o) {return o.optString(key);}
}
class JSONNumericKey extends Sorter.Key<JSONObject, Double> {
final String key;
JSONNumericKey(String key) {this.key = key;}
public Double get(JSONObject o) {return o.optDouble(key);}
}
...
// sort by price descending then name ascending
final Key<JSONObject, ?> keys[] = { new JSONNumericKey("price"), new JSONStringKey("name") };
sort(list, keys, new int[]{-1, 1});
它警告final Comparable av = key[i].get(a), bv = key[i].get(b);
和av
使用原始类型声明:bv
而不是Comparable
。他们是。但是,如果我将类型更改为Comparable<?>
,则下一行Comparable<?>
会失败,因为两个不同的av.compareTo(bv)
不一定是同一类型。在我的具体实现中,他们将,但我不知道如何向类型系统表达。
如何告诉类型系统Comparable<?>
和av
具有完全相同的类型?我无法修复&#34;它通过给出一个特定的类型(例如bv
),因为在我的例子中,循环中的第一个键返回Comparable<String>
(实现String
)并且循环中的第二个键返回{{ 1}}(实现Comparable<String>
)。
我可以在Double
行和Comparable<Double>
行@SuppressWarnings("rawtypes")
上写key[i].get()
,但我希望尽可能检查类型。
编辑:感谢davmac的回答,创建一个修复特定类似类型的中间方法可以正常工作:
@SuppressWarnings("unchecked")
答案 0 :(得分:2)
您需要使用类型参数来表示两个“未知”类型是相同的。我想也许你应该改变你的方法签名:
static <T> void sort(List<T> data, final Key<T, ?> key[], final int dir[])
到
static <T,U> void sort(List<T> data, final Key<T,U> key[], final int dir[])
但是,我不认为这适用于您的完整示例,因为键中的元素不是相同的类型:
// sort by price descending then name ascending
final Key<JSONObject, ?> keys[] = { new JSONNumericKey("price"), new JSONStringKey("name") };
sort(list, keys, new int[]{-1, 1});
因此,您可以将分拣机中的相关部分提取到通用方法中:
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
final Comparable<?> av = key[i].get(a), bv = key[i].get(b);
final int cmp = doCompareTo(av, bv);
if (cmp != 0) return cmp * dir[i];
}
return 0;
}
private <U extends Comparable<U>> int doCompareTo(U a, U b) {
return a.compareTo(b);
}
...但这也不起作用,因为Comparable<U>
不一定是extend U
。问题是您的密钥返回Comparable<V>
,但您要比较其中两个;那是不可能的。 Comparable<V>
可以与V
进行比较,但不能与其他Comparable<V>
进行比较。
一般来说,这里有太多问题可以为您提供简单的解决方案。您需要完全重新考虑类型。例如,如果您希望Key
的get方法返回与彼此相似的对象,那么它应该返回V
而不是Comparable<V>
。
我希望上述建议至少指出你正确的方向。
答案 1 :(得分:0)
你现在宣布它们的方式,你可以使用私人捕获助手:
static <T> void sort(List<T> data, final Key<T, ?> key[], final int dir[]) {
Collections.sort(list, new Comparator<T>() {
public int compare(T a, T b) {
for (int i = 0; i < key.length; i++) {
final int cmp = compareHelper(key[i], a, b);
if (cmp != 0) return cmp * dir[i];
}
return 0;
}
});
}
private static <T, U extends Comparable<U>> int compareHelper(Key<T, U> k, T a, T b) {
U av = k.get(a), bv = k.get(b);
return av.compareTo(bv);
}
或者您可以完全摆脱V
的{{1}}并仅对Key
进行约束,这将在sort
上进行参数设置:
U