Newbie
Newbie

Reputation: 1724

Dynamically sort a list based on attribute selected by user in Java 8

I am trying to sort a list with its member attribute by using Comparator.comparing(), and the attribute is selected by the user. Consider the case below:

public class MyClass extends BaseClass
{
    private String attr1;
    private Date attr2;
    private ChildClass attr3;

    //getter and setter
}

public class ChildClass extends BaseClass
{
    private String attr1;
    private Date attr2;
    private int attr3;

    //getter and setter
}

This is what I have tried, but having compile error.

private Map<String, Function<MyClass, ?>> sortingOptions = new HashMap<>();
private String sortBy;  //sorting attribute selected by user

@PostConstruct
public void init()
{
    //my list to be sort
    List<MyClass> list = myService.getList();

    sortingOptions.put("attr1", MyClass::getAttr1);
    sortingOptions.put("attr2", MyClass::getAttr2);
    //......
}

//listener for sorting option changed
public void sortOptionChangedListener()
{
    //this line of code having error
    list.sort(Comparator.comparing(sortingOptions.get[sortBy]));
}

The error showing is

The method comparing(Function<? super T,? extends U>) in the type Comparator is not applicable for the arguments (Function<MyClass,capture#3-of ?>)

Upvotes: 3

Views: 2612

Answers (3)

Shark
Shark

Reputation: 23

I don't think Clinton's right. The following is the Comparator::comparing source code.

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
        Function<? super T, ? extends U> keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

Its parameters is a function whose input parameter is T and the return value is Comparable, this does not apply to a simple getter. I think reflection can do this, but more complex.

Upvotes: 0

Jai
Jai

Reputation: 8363

You can do this:

private Map<String, Function<MyClass, ? extends Comparable>> sortingOptions = new HashMap<>();

You will probably have to live with warnings, one at the sortingOptions, another at list.sort().

If you want this to be neater, you can make a helper method.

@SuppressWarnings("unchecked")
private <T, U extends Comparable<U>> Function<? super T, ? extends U> getComparator(String sortBy) {
    return (Function<T, U>) sortingOptions.get(sortBy);
}

Usage:

list.sort(Comparator.comparing(getComparator(sortBy)));

Update

You should do what Clinton recommended, that approach is more clean.

Upvotes: -1

Clint Munden
Clint Munden

Reputation: 344

It may be easier to simply store the method references in your Map rather than the Functions that you have now. Consider this...

public class BaseClass implements Comparable<BaseClass> {
    @Override
    public int compareTo(BaseClass o) {
        // implement this properly
        return 0;
    }
}

public class MyClass extends BaseClass {
    private String attr1;
    private Date attr2;
    private ChildClass attr3;

    //getter and setter
}

public class ChildClass extends BaseClass
{
    private String attr1;
    private Date attr2;
    private int attr3;

    //getter and setter
}

private List<MyClass> list;
private Map<String, Comparator<? super MyClass>> sortingOptions = new HashMap<>();
private String sortBy;  //sorting attribute selected by user

@PostConstruct
public void init()
{
    //my list to be sort
    list = myService.getList();

    sortingOptions.put("attr1", Comparator.comparing(MyClass::getAttr1));
    sortingOptions.put("attr2", Comparator.comparing(MyClass::getAttr2));
    sortingOptions.put("attr3", Comparator.comparing(MyClass::getAttr3));
    //......
}

public void sortOptionChangedListener()
{
    list.sort(sortingOptions.get(sortBy));
}

Upvotes: 8

Related Questions