I have an application where a user provides me with the name of a field, e.g name
or costInCents
, and I have to sort by that field. I have ways of guaranteeing that the field name will be correct. This application causes the complication that I simply cannot make my class Comparable
and implement a specific compareTo()
, since with a custom implementation of compareTo()
I need to know which fields / methods to use at implementation time.
So to achieve this goal, I am trying to use reflection in order to match the field to its accessor. Here’s a MWE of what I want to do.
Class Product
is a simple POJO class whose instances I want to pairwise compare:
public class Product { final String name; final Integer quantity; final Long costInCents; public Product(final String name, final Integer quantity, final Long costInCents) { this.name = name; this.quantity = quantity; this.costInCents = costInCents; } public String getName() { return name; } public Integer getQuantity() { return quantity; } public Long getCostInCents() { return costInCents; } }
And my Main
class, which is currently incomplete:
public class Main { public static void main(String[] args) { final Product[] productArray = { new Product("Clorox wipes", 50, 700L), new Product("Desk chair", 10, 12000L), new Product("TV", 5, 30000L), new Product("Bookcase", 5, 12000L), new Product("Water bottle", 20, 700L), }; // The following void methods are supposed to sort in-place with something like Arrays.sort() or Collections.sort(), // but I am also open to solutions involving stuff like Stream::sorted() or similar ones, which return a sorted array. sortByField(productArray, "costInCents"); sortByField(productArray, "name"); } private void sortByField(final Product[] productArray, final String sorterFieldName) { final Field sorterField = getSorterField(sorterFieldName, LiteProduct.class); // Gets the Field somehow final Method sorterAccessor = getSorterAccessor(sorterField, LiteProduct.class); // Given the Field, this is easy Arrays.sort((Product p1, Product p2)->((Comparable<?>)sorterAccessor.invoke(p1)).compareTo(sorterAccessor.invoke(p2)) > 0); // Capture of ? instead of Object } }
Unfortunately, the Arrays.sort()
line results in a compile-time error with message Capture of ? instead of Object
. I have tried casting the second argument to Comparable<?>
, Comparable<? super sorterField.getType()
, etc, with no luck. Ideas?
Advertisement
Answer
Possibly the best way – with sorting strategies. No need for reflection, compatible with more complex sorting logic:
Map<String, Comparator<Product>> sortingStrategies = new HashMap<>(){ { put("costInCents", Comparator.comparingLong(p->p.costInCents)); put("quantity", Comparator.comparingLong(p->p.quantity)); put("name", Comparator.comparing(p->p.name)); } }; private void sortByField(final Product[] productArray, final String sorterFieldName) { Arrays.sort(productArray, sortingStrategies.get(sorterFieldName)); }