René Link
René Link

Reputation: 51473

Cast the return type of a lambda expression and sort a list with a custom comparator

I have a List<DefaultMutableTreeNode> and want to sort the list by a property of the userObject with a custom comparator.

In order to provide a simple example I will use the user object Person

public class Person {
    private String firstname;
    private String lastname;

    public Person(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

    public String getFirstname() {
        return firstname;
    }

    public String getLastname() {
        return lastname;
    }

}

and a custom comparator

public class PersonComparator implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        // to keep the example simple I just compare the firstnames here
        return o1.getFirstname().compareTo(o2.getFirstname());
    }

}

My first thought was to use Comparator.comparing with a method reference as key extractor and a key comparator.

public static void main(String[] args) {
    List<DefaultMutableTreeNode> nodes = new ArrayList<DefaultMutableTreeNode>();

    nodes.add(new DefaultMutableTreeNode(new Person("James", "Smith")));
    nodes.add(new DefaultMutableTreeNode(new Person("Paul", "Clark")));
    nodes.add(new DefaultMutableTreeNode(new Person("Anthony", "Brown")));

    Comparator<Person> myComparator = new PersonComparator();
    nodes.sort(Comparator.comparing(DefaultMutableTreeNode::getUserObject, myComparator));

}

But this gives me (of course) a compiler error since the DefaultMutableTreeNode.getUserObject() returns an Object and not a Person.

How can I cast the retrun type of the lambda method reference DefaultMutableTreeNode::getUserObject?

Or do you have any other options?

EDIT:

I can do it this way, but is there an easier way? (in one line)

Function<DefaultMutableTreeNode, Object> userObject = DefaultMutableTreeNode::getUserObject;
nodes.sort(Comparator.comparing(userObject.andThen(Person.class::cast), myComparator));

Upvotes: 1

Views: 1630

Answers (1)

Tunaki
Tunaki

Reputation: 137289

Your question talks about "lambda method reference" but this doesn't exist: there are lambda expressions, and there are method-references. A method-reference is a special feature where, instead of writing a lambda expression that would simply invoke another existing method, you use the :: operator to refer to that method directly by name.

But in this case, since getUserObject returns an Object and not a Person, you won't be able to directly use a method-reference here: you need to have more code than simply refering to getUserObject: a cast is needed.

As such, use a lambda expression:

nodes.sort(Comparator.comparing(t -> (Person) t.getUserObject(), myComparator));

Another way would be to create a method that returns a Person instead of an Object, and refer to that method:

private static Person getPerson(DefaultMutableTreeNode node) {
    return (Person) node.getUserObject();
}

// assuming "getPerson" in a class called MyClass
nodes.sort(Comparator.comparing(MyClass::getPerson, myComparator)); 

Note that the PersonComparator can be written more simply with

Comparator<Person> myComparator = Comparator.comparing(Person::getFirstname);

Upvotes: 4

Related Questions