Thach Huynh
Thach Huynh

Reputation: 1293

Sort a List<Object> based on field name by reflection

I have a model like that :

public class Contact {

    private int id;

    private String firstName;

    private String lastName;

    private String address;

    private String city;

    private String state;

    private String zipCode;

    private String mobilePhone;

    private String email;

    private String dayOfBirth;

    private int age;

    public String toLine() {
        return String.format("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", id, firstName, lastName, dayOfBirth, address, city, state, zipCode, mobilePhone, email);
    }
}

I write a sort function to sort List by field name by using reflection but field.get(contact1) have no method compareTo. Any way to achieve it ? BTW, any way to make toLine function shorter ? it seem too long.

    public void sortByFieldName(String fieldName, List<Contact> validContacts) throws NoSuchFieldException {

        Field field = Contact.class.getDeclaredField(fieldName);
        field.setAccessible(true);

        validContacts.stream().sorted((contact1, contact2) -> field.get(contact1).compareTo(field.get(contact2)));
}

I do not want to use this because it seem to be not flexible:

if (fieldName.equals("zipCode")) {
            validContacts.sort(Comparator.comparing(Contact::getZipCode));
        }

I removed the getters and setters because it's too long

Upvotes: 0

Views: 2594

Answers (1)

Ingo B&#252;rk
Ingo B&#252;rk

Reputation: 20043

You don't need any reflection at all. You can use Comparator#comparing for that:

List<Contact> sortedContacts = validContacts.stream()
    .sorted(Comparator.comparing(Contact::getZipCode))
    .collect(Collectors.toList());

Of course this assumes that your class has getter methods, which it didn't in your question. If this is the case and the fields are accessible, you can also use c -> c.zipCode instead of Contact::getZipCode. If neither is the case and the class really looks like what you've shown, the fields would be useless as no one would have access to them (unless it's a static class within another class).


If you absolutely do need to do this using reflection – which I really think you don't –, then you can do it like this. But I'd say this is much more specific and not reusable whatsoever. Using the stream API as shown above would be better:

public static List<Contact> sortByFieldName(List<Contact> list, String fieldName) throws NoSuchFieldException {
    Field field = Contact.class.getDeclaredField(fieldName);
    if (!String.class.isAssignableFrom(field.getType())) {
        throw new IllegalArgumentException("Field is not a string!");
    }

    field.setAccessible(true);
    return list.stream()
        .sorted((first, second) -> {
            try {
                String a = (String) field.get(first);
                String b = (String) field.get(second);
                return a.compareTo(b);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Error", e);
            }
        })
        .collect(Collectors.toList());
}

Upvotes: 3

Related Questions