4

I have a statement:

searchResults.sort(Comparator.comparing(WCCTableRowData::getD));

where getD is an accessor in the class WCCTableRowData and searchResults is a list of WCCTableRowData. The WCCTableRowData class has accessors from getA through getZ. I need to be able to set the sort field on the fly from a passed in variable. Is there an elegant way to do this or will I need a series of if statements or similar?

UPDATE 1 Unfortunately, neither approach in the accepted answer worked though I think in general the direction is correct. With approach 2 I get:

Intelli Sense

With approach 1, row.getField does not pick up the getField method in WCCTableRowData class and I get similar "does not conform to upper bound(s)" error. I think the error is saying that WCCTableRowData class has to implement Comparable?

5
  • 3
    Just pass in a Function<WCCTableRowData, Comparable<?>>, call it extractor. Then you can simply do searchResults.sort(Comparator.comparing(extractor)). Commented Aug 18, 2016 at 22:12
  • 1
    Suggestion: it is always better to paste the stacktrace as code block instead of an screenshot Commented Aug 20, 2016 at 2:09
  • I have just found out that at least in Eclipse Neon with ECJ Java compiler, usage of Comparable<?> in solution #2 leads to severe type issues like the one you posted about. To prevent such issues, one may change type reference to just Comparable (raw type) in getters definition. You will get type warnings in variable definition, of course, but you may easily overcome those by prepending a @SuppressWarnings: @SuppressWarnings("rawtypes") Map<String, Function<WCCTableRowData, Comparable>> getters = ... Unchecked conversion warning in comparing() could be suppressed as well. Commented Aug 20, 2016 at 21:07
  • Please note that the same holds for solution #1: just change return type of getField to Comparable (Comparable getField(final String name) {...}) and you will have a working solution. Commented Aug 20, 2016 at 21:16
  • @rpax. I agree but in this case it was JDeveloper intellisense during design time and I could not copy out the text from the info bubble. Commented Aug 22, 2016 at 16:57

2 Answers 2

4

One way is to add a method in WCCTableRowData that can be given a field name and returns the value of that field.

class WCCTableRowData {
    Comparable<?> getField(String name) { ... }
}

String name = "C";
searchResults.sort(Comparator.comparing(row -> row.getField(name)));

If you don't want to modify the class, then you could set up an external map.

Map<String, Function<WCCTableRowData, Comparable<?>>> getters = new HashMap<>();
getters.put("A", WCCTableRowData::getA);
getters.put("B", WCCTableRowData::getB);
getters.put("C", WCCTableRowData::getC);

String name = "C";
searchResults.sort(Comparator.comparing(getters.get(name)));
Sign up to request clarification or add additional context in comments.

1 Comment

Make sure to make getters a static final field, preferably wrapped in Collections.unmodifiableMap().
4

One way would be to store method references in a map with keys - values of your variable. It would be analogue of switch statement. You can use guava ImmutableMap.<WCCTableRowData, Comparable<?>>of() to make it a bit nicer.

Update

You can explicitly tell that comparator which is constructed by Comparator.comparing(...) is comparing Comparable :

Comparator.<WCCTableRowData, Comparable>comparing(getters.get(name));

Also you can just store comparators of your object (sounds much more reasonable but adds boilerplate), not functions returning comparables:

Map<String, Comparator<WCCTableRowData>> comparators = new HashMap<>();
comparators.put("A", Comparator.comparing(WCCTableRowData::getX));
comparators.put("B", Comparator.comparing(WCCTableRowData::getY));

String name = "C";
searchResults.sort(comparators.get(name)); 

1 Comment

Nice! To me, this approach is more general than the former, since key "A" becomes not just a field designator, but rather a way to describe a whole comparator strategy, made from linking different comparator in chains. For example, you could add this key: comparators.put("XY", Comparator.comparing(WCCTableRowData::getX).thenComparing(WCCTableRowData::getY); and have "XY" as a key to refer to this particular ordering, which is not doable in the previous approach. For some (non-trivial) cases this may prove useful, thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.