user4359659
user4359659

Reputation: 159

Compare two objects in Java using reflection and annotations

I have a sample Student object, which has several fields (index number, name, family name, etc.). I'd like to compare two Student objects, but I'd like to define what is the priority of field comparison. For this purpose, I use field annotations, like in the example below:

Annotation interface definition:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CompareOrder {
    int order();    
}

The usage:

@CompareOrder(order = 3)
private int indexNumber;
@CompareOrder(order = 2)
private String name;
@CompareOrder(order = 1)
private String familyName;
private ArrayList <Float> marks;
@CompareOrder(order = 5)
private float average;
@CompareOrder(order = 4)
private boolean fullTime;

Some fields contain no annotation, and are ignored in comparison. The first thing I did, is extracting all fields of the class using reflection and sorting them according to the comparison order, which corresponds to the first part of my compareTo overriden function body:

@Override
public int compareTo(Student other) {
    // TODO Auto-generated method stub
    Class<? extends Student>  clazz = this.getClass();
    Field[] fields = clazz.getDeclaredFields();
    List <Field> fieldList = new ArrayList<Field> ();
    //ommiting unannotated fields
    for (Field f : fields)
    {
        if(f.getAnnotation(CompareOrder.class) != null) fieldList.add(f);
    }
    //sorting fields by compare order
    Collections.sort(fieldList, new Comparator <Field> (){

        @Override
        public int compare(Field f0, Field f1) {
            CompareOrder compOrder0 = f0.getAnnotation(CompareOrder.class);             
            CompareOrder compOrder1 = f1.getAnnotation(CompareOrder.class);             
            return compOrder0.order() - compOrder1.order();

        }
    });

    for (Field f : fieldList)
    {
        // HERE GOES COMPARISON OF OBJECTS FIELD BY FIELD, ACCORDING TO THE ORDERING
    }

    return 0;
}

And now I'm kind of stuck. Because at this point, I need to decide whether a field of this and other student is greater or smaller. And I can't do that with raw Object type, only thing I can do is call equals method, which is not enough. I need to know if it's greater or smaller. I need to cast it somehow to... yeah, what exactly? How do I extract the field type, and then cast? Is this even doable?

PS. I know, that possibly an easier approach is to use method annotations and invoke getter methods, but I'm just curious whether it is doable the way I'm trying to do it.

Upvotes: 1

Views: 5531

Answers (2)

Elliott Frisch
Elliott Frisch

Reputation: 201497

I think you can use Class.isAssignableFrom(Class) and something like

Class<Comparable> cls = Comparable.class;
for (Field f : fieldList) {
    if (cls.isAssignableFrom(f.getType())) {
        Comparable c = (Comparable) f.get(this);
        int r = c.compareTo((Comparable) f.get(other));
        if (r != 0) {
            return r;
        }
    }
}

Upvotes: 1

Abacus
Abacus

Reputation: 19471

As you're already into reflection you can check if the fields that you annotate also implement the Comparable interface. Then you can compare them with the compareTo method that they implement.

If you have a field that's annotated and does not implement Comparable, then that should be an error (is it possible to restrict the annotation to fields implementing a special interface?).

Change the int fields to Integer and the other primitive types as well to their object counterparts and don't forget the null check when comparing.

Upvotes: 0

Related Questions