The answer that you linked to basically contains the core idea, even though it refers to fields ("properties") and not to methods: Create a way of obtaining the desired value from the object, and then simply compare these values for two objects.
One could consider it as a duplicate, but I'm not sure.
In any case:
You should carefully think about whether reflection is the right approach here.
It might be much more elegant (i.e. less hacky) to generate the required comparators without reflection. This could be an enum where you attach the proper Comparator
to each enum value:
enum TaskProperty {
TITLE(comparatorForTitle),
DATE(comparatorForDate), ...
}
// Using it:
tasks.stream().sorted(TaskProperty.TITLE.getComparator()).forEach(...);
Or maybe (less type safe, but a tad more flexible), using a map where you can look them up via a string, as in
// Put all comparators into a map
map.put("getDate", compareTaskByDate);
...
// Later, use the string to pick up the comparator from the map:
tasks.stream().sorted(map.get("getDate")).forEach(...);
If you have carefully thought this through, and really want to use the reflection based approach:
You can create a utility method that obtains the return value of a certain method for a given object. Then create a comparator that calls this method for the given objects, and compares the return values. For flexibility sake, you can pass the return values to a downstream comparator (which can be naturalOrder
by default).
An example of how this could be done is shown here. But the list of exceptions that are caught in getOptional
should make clear that many things can go wrong here, and you should really consider a different approach:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class SortWithReflection
{
public static void main(String[] args)
{
List<Task> tasks = new ArrayList<Task>();
tasks.add(new Task("AAA", 222));
tasks.add(new Task("BBB", 333));
tasks.add(new Task("CCC", 111));
System.out.println("By getTitle:");
tasks.stream()
.sorted(by("getTitle"))
.forEach(System.out::println);
System.out.println("By getDate:");
tasks.stream()
.sorted(by("getDate"))
.forEach(System.out::println);
System.out.println("By getDate, reversed");
tasks.stream()
.sorted(by("getDate", Comparator.naturalOrder().reversed()))
.forEach(System.out::println);
}
private static <T> Comparator<T> by(String methodName)
{
return by(methodName, Comparator.naturalOrder());
}
private static <T> Comparator<T> by(
String methodName, Comparator<?> downstream)
{
@SuppressWarnings("unchecked")
Comparator<Object> uncheckedDownstream =
(Comparator<Object>) downstream;
return (t0, t1) ->
{
Object r0 = getOptional(t0, methodName);
Object r1 = getOptional(t1, methodName);
return uncheckedDownstream.compare(r0, r1);
};
}
private static <T> T getOptional(
Object instance, String methodName)
{
try
{
Class<?> type = instance.getClass();
Method method = type.getDeclaredMethod(methodName);
Object object = method.invoke(instance);
@SuppressWarnings("unchecked")
T result = (T)object;
return result;
}
catch (NoSuchMethodException
| SecurityException
| IllegalAccessException
| IllegalArgumentException
| InvocationTargetException
| ClassCastException e)
{
e.printStackTrace();
return null;
}
}
static class Task
{
String title;
Integer date;
Task(String title, Integer date)
{
this.title = title;
this.date = date;
}
String getTitle()
{
return title;
}
Integer getDate()
{
return date;
}
@Override
public String toString()
{
return title + ": " + date;
}
}
}