我有很多这样的类:
Class Person {
protected String name;
protected String surname;
...getters and setters...
}
我想按姓名或姓氏订购一本人物选集。实际上我只是这样做:
Collections.sort(listofpersons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) { return p1.getName().compareTo(p2.getName()); }
})
或
Collections.sort(listofpersons, new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) { return p1.getSurname().compareTo(p2.getSurname()); }
})
我正在尝试实现一个通用比较器,如:
MyUtils.sort(listofpersons,"getName");
MyUtils.sort(listofpersons,"getSurame");
,我正试图理解如何用泛型和反射来做它,但我被卡住了。我正在这样做:
public static <T> void sortCollection(Collection<T> list, final String methodName) {
Comparator<T> comparator = new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
try {
String a=o1.getClass().getMethod(methodName).invoke(o1).toString();
String b=o2.getClass().getMethod(methodName).invoke(o2).toString();
return a.compareTo(b);
} catch (Exception ex) {
...log somewhere...
return 0;
}
}
};
Collections.sort(list, comparator);
}
暂时忘记catch Exception和String类型转换,重点是Collections.sort(Collection,Comparator)不存在,但我不知道要创建一个Comparator…我很想知道我的想法是否有意义(以及为什么),什么是实现它的正确方法。
谢谢!
问题在于sortCollection
方法的签名。将"list
"参数的类型由"Collection<T>
"改为"List<T>
"。换句话说,你的方法头应该是:
public static <T> void sortCollection(List<T> list, final String methodName)
这是因为Collections.sort()
期望它的第一个参数是List
(参见它的javadoc)
关于你的实现,有一些你可以使用的通用/反射技巧。这是一个可能的替代impl。
@SuppressWarnings({ "unchecked", "rawtypes" })
public static<T> Comparator<T>
newMethodComparator(Class<T> cls, String methodName) throws Exception {
Method method = cls.getMethod(methodName);
if (method.getParameterTypes().length != 0)
throw new Exception("Method " + method + " takes parameters");
Class<?> returnType = method.getReturnType();
if (!Comparable.class.isAssignableFrom(returnType))
throw new Exception("The return type " + returnType + " is not Comparable");
return newMethodComparator(method, (Class<? extends Comparable>) returnType);
}
private static<T,R extends Comparable<R>> Comparator<T> newMethodComparator(
final Method method, final Class<R> returnType) throws Exception {
return new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
try {
R a = invoke(method, o1);
R b = invoke(method, o2);
return a.compareTo(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private R invoke(Method method, T o) throws Exception {
return returnType.cast(method.invoke(o));
}
};
}
你可以这样使用它:
List<Person> ps = new ArrayList<>(Arrays.asList(
new Person("A", "D"), new Person("B", "C")));
...
Comparator<Person> byNameComparator =
newMethodComparator(Person.class, "getName");
Collections.sort(ps, byNameComparator);
原理:
(a)这意味着。将适用于任何返回Comparable
对象的方法。您的实现使用了字符串比较,这将不能很好地工作,例如,如果该方法返回int。
(b)这意味着。检查方法的返回类型以及它在创建Comparator
时(即在排序开始之前)接受零参数的事实。因此,Collections.sort()
向您抛出异常的可能性较小。
(c)注意@SuppressWarnings({ "unchecked", "rawtypes" })
不构成任何实际风险。如果方法的返回类型是Comparable
,那么向下转换将成功。如果返回类型不是Comparable
,则Comparable.class.isAssignableFrom(returnType))
将返回false,该方法将抛出显式异常,并且永远不会到达向下强制转换。
不能对Collection进行排序,只能对List进行排序。这是因为不是所有的集合(例如Set)都支持元素"order"的概念。
将方法签名更改为:
public static <T> void sort(List<T> list, final String methodName) {
我倾向于说,您可能最好只是创建不同比较器的具体实现,类似于您在问题开头所展示的。
public class PersonNameComparator implements Comparator<Person>
{
public int compareTo(Person o1, Person o2)
{
return o1.getName().compareTo(o2.getName());
}
}
public class PersonSurnameComparator implements Comparator<Person>
{
public int compareTo(Person o1, Person o2)
{
return o1.getSurname().compareTo(o2.getSurname());
}
}
然后调用标准库Collections排序方法
Collections.sort(listofpersons, new PersonNameComparator());
Collections.sort(listofpersons, new PersonSurameComparator());
这可能有点代码的味道,因为它是大量的碗板代码,你最终会让IDE生成,但它至少有一个优点,很容易编写一些单元测试,并且比使用反射性能更好(如果只是一点点)。
这种方法的缺点是,每次向Person类添加新方法时,都必须实现一个新的比较器,因此这里有很多内容。
如果'T'包含其他复杂类型并且不可比较,则下面的解决方案可能还需要更多的检查。
public <T> void sortList(List<T> objList, String[] attributes) throws ClassNotFoundException {
Comparator<T> comparator = null;
Comparator<T> temp = null;
if (objList != null) {
Class classObj = objList.get(0).getClass();
Method[] methods = classObj.getMethods();
for(String attribute : attributes) {
for(Method method : methods) {
if(method.getName().contains("get") && method.getName().toLowerCase().contains(attribute.toLowerCase()))
temp = new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
try {
Comparable v1 = (Comparable) method.invoke(o1);
Comparable v2 = (Comparable) method.invoke(o2);
return v1.compareTo(v2);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return 0;
}
};
if (temp != null)
if (comparator == null)
comparator = temp;
else
comparator = comparator.thenComparing(temp);
}
}
if (comparator != null)
objList.sort(comparator);
}
}