Sort a List of Objects based on runtime property
Create a Comparator
for the job:
public class EmployeeComparator implements Comparator<Employee> {
private final String type;
public EmployeeComparator (String type) {
this.type = type;
}
public int compare(Employee e1, Employee e2) {
if (type.equals("name")) {
return e1.getName().compareTo(e2.getName());
}
return e1.getId().compareTo(e2.getId());
}
}
Then to use it
String type = "name"; // determined at runtime
Collections.sort(list, new EmployeeComparator(type));
The reflective version would be similar, except you would look for a method on the object of "get" + type (capitalised) and invoke that and hard cast it to Comparable and use compareTo (I'll try to show the code, but I'm using my iPhone and its a bit of a stretch, but here goes)
public class DynamicComparator implements Comparator<Object> {
private final String type;
// pass in type capitalised, eg "Name"
// ie the getter method name minus the "get"
public DynamicComparator (String type) {
this.type = type;
}
public int compare(Object o1, Object o2) {
// try-catch omitted
Method m = o1.getClass().getMethod("get" + type);
String s1 = (String)m.invoke(o1);
String s2 = (String)m.invoke(o2);
return s1.compareTo(s2);
}
}
OK... Here's how to do it without creating a class, using an anonymous class (with exception handling so code compiles):
List<?> list;
final String attribute = "Name"; // for example. Also, this is case-sensitive
Collections.sort(list, new Comparator<Object>() {
public int compare(Object o1, Object o2) {
try {
Method m = o1.getClass().getMethod("get" + attribute);
// Assume String type. If different, you must handle each type
String s1 = (String) m.invoke(o1);
String s2 = (String) m.invoke(o2);
return s1.compareTo(s2);
// simply re-throw checked exceptions wrapped in an unchecked exception
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
});
Do the following:
- get the name of the field from the client
- build the name of the getter -> "get" + field name (after capitalizing the first character)
- try to find the method with reflection by using Class.getDeclaredMethod()
- if found, invoke the returned Method object on two instances of your VO class
- use the results of the invoked getter methods for sorting